NOIP2007题解
2016-05-20 12:02
459 查看
统计数字:
题目大意:给你n个数,求每个数出现的次数。
题解:排个序,记录每个数出现的个数,如果碰到不同的数了输出并清零即可。时间复杂度:O(n log n),空间复杂度:O(n)。
字符串的展开:
题目大意:
给你一个字符串,只有小写字母,数字和’-’,给定三个参数,当出现’-’时,s1,s2,s3按以下规则展开:
1.在输入的字符串中,出现了减号’-’,减号两侧
同为小写字母或同为数字,且按照ASCII 码的顺序,减号右边的字符严格大于左边的字符。
2.参数p1:展开方式。p1=1 时,对于字母子串,填充小写字母;p1=2 时,对于字母子串,
填充大写字母。这两种情况下数字子串的填充方式相同。p1=3 时,不论是字母子串还是数字子串,都用与要填充的字母个数相同的星号’*’来填充。
3.参数p2:填充字符的重复个数。p2=k 表示同一个字符要连续填充k 个。例如,当p2=3
时,子串“d-h”应扩展为”deeefffgggh”。减号两侧的字符不变。
4.参数p3:是否改为逆序:p3=1 表示维持原有顺序,p3=2 表示采用逆序输出,注意这时
仍然不包括减号两端的字符。例如当p1=1、p2=2、p3=2 时,子串”d-h”应扩展为”dggffeeh”。
5.如果减号右边的字符恰好是左边字符的后继,只删除中间的减号,例如:”d-e”应输出
为”de”,”3-4”应输出为”34”。如果减号右边的字符按照ASCII码的顺序小于或等于左边字符,输出时,要保留中间的减号,例如:”d-d”应输出为”d-d”,”3-1”应输出为”3-1”。len<=100.
题解:
直接模拟,注意判断几种特殊情况:’-’两边有一边为’-’,’-’在首尾的位置。时间复杂度:O(len*p2),空间复杂度:O(len)。
矩阵取数游戏:
题目大意:给定一个n*m的矩阵,取m次,每次在每一行取一个数,只能在每一行的首尾位置取,第i次的得分为取的n个数的总和*2^i。求m次取数后的最大得分值。n,m<=80,aij<=1000.
题解:
unsigned long long的最大值为2^64-1,所以显然这题不管什么算法都得套一个高精度……
由秦九韶公式可知:我们从内往外取数的话就每次对于当前ans*2即可。而从内往外取数的话有一个好处:每个数都有可能为最后一个取的,然后接下来由这个数往左右扩展一个就得到了最后两次取的数,一直扩展到左右两边即可。
这样子就变成了一个经典的区间DP了。以长度为第一重循环,对于长度为len-1的最优取法我们都已经得到了,而对于一段要取的序列l,r,要么就是先取l,r-1,然后最后取r,或者先取l+1,r,最后取l,两者取个max即可。
设f[i][j]代表从l开始长度为j的数列的最大得分值,则:
f[i][j]=max(2*(f[i][j-1]+a[j]),2*(f[i+1][j-1]+a[i]))。时间复杂度:O(n*m*m*G),空间复杂度:O(m*m*G)。
树网的核:
题目大意:给定一棵树,一段路径记为F,D(i,F),代表i到F上最近的点的距离,一段路径的偏心距为max(D(i,F))(i<=n)。求树直径上的一段长度<=x的路径的偏心距的最小值。n<=300.
题解:
首先dfs一遍求出相邻点两两之间的距离,再用floyd求出所有点对间距离,并求出所有直径。我们需要知道这两个定理:1.一条路径上的偏心距必定是由某条直径的端点到这条路径上的距离。2.核一定是所有直径的交集的一部分。因此我们可以任取一条直径,对于该直径上的一段路径,判断是否<=s,然后求出该直径的端点与该路径的距离,更新答案即可。时间复杂度:O(n^3),空间复杂度:O(n^2)。然而这题数据太水了,怎么做都能过。
题目大意:给你n个数,求每个数出现的次数。
题解:排个序,记录每个数出现的个数,如果碰到不同的数了输出并清零即可。时间复杂度:O(n log n),空间复杂度:O(n)。
#include<cstdio> #include<algorithm> using namespace std; int n,i,a[200010],x,cnt; int main(){ scanf("%d",&n); for(i=1;i<=n;i++)scanf("%d",&a[i]); sort(a+1,a+1+n); printf("%d",a[1]),cnt=1; for(i=2;i<=n;i++) if(a[i]==a[i-1])cnt++; else printf(" %d\n%d",cnt,a[i]),cnt=1; printf(" %d",cnt); return 0; }
字符串的展开:
题目大意:
给你一个字符串,只有小写字母,数字和’-’,给定三个参数,当出现’-’时,s1,s2,s3按以下规则展开:
1.在输入的字符串中,出现了减号’-’,减号两侧
同为小写字母或同为数字,且按照ASCII 码的顺序,减号右边的字符严格大于左边的字符。
2.参数p1:展开方式。p1=1 时,对于字母子串,填充小写字母;p1=2 时,对于字母子串,
填充大写字母。这两种情况下数字子串的填充方式相同。p1=3 时,不论是字母子串还是数字子串,都用与要填充的字母个数相同的星号’*’来填充。
3.参数p2:填充字符的重复个数。p2=k 表示同一个字符要连续填充k 个。例如,当p2=3
时,子串“d-h”应扩展为”deeefffgggh”。减号两侧的字符不变。
4.参数p3:是否改为逆序:p3=1 表示维持原有顺序,p3=2 表示采用逆序输出,注意这时
仍然不包括减号两端的字符。例如当p1=1、p2=2、p3=2 时,子串”d-h”应扩展为”dggffeeh”。
5.如果减号右边的字符恰好是左边字符的后继,只删除中间的减号,例如:”d-e”应输出
为”de”,”3-4”应输出为”34”。如果减号右边的字符按照ASCII码的顺序小于或等于左边字符,输出时,要保留中间的减号,例如:”d-d”应输出为”d-d”,”3-1”应输出为”3-1”。len<=100.
题解:
直接模拟,注意判断几种特殊情况:’-’两边有一边为’-’,’-’在首尾的位置。时间复杂度:O(len*p2),空间复杂度:O(len)。
#include<cstdio> #include<cstring> int i,n,p1,p2,p3,j,k; char s[110]; void putsz(){ if(p1==3) for(j=s[i-1]+1;j<s[i+1];j++) for(k=1;k<=p2;k++)putchar('*'); else if(p3==1) for(j=s[i-1]+1;j<s[i+1];j++) for(k=1;k<=p2;k++)putchar(j); else for(j=s[i+1]-1;j>s[i-1];j--) for(k=1;k<=p2;k++)putchar(j); } void putch(){ if(p1==3) for(j=s[i-1]+1;j<s[i+1];j++) for(k=1;k<=p2;k++)putchar('*'); else if(p3==1){ if(p1==1) for(j=s[i-1]+1;j<s[i+1];j++) for(k=1;k<=p2;k++)putchar(j); else for(j=s[i-1]+1;j<s[i+1];j++) for(k=1;k<=p2;k++)putchar(j-'a'+'A'); } else{ if(p1==1) for(j=s[i+1]-1;j>s[i-1];j--) for(k=1;k<=p2;k++)putchar(j); else for(j=s[i+1]-1;j>s[i-1];j--) for(k=1;k<=p2;k++)putchar(j-'a'+'A'); } } int main(){ scanf("%d%d%d",&p1,&p2,&p3); scanf("%s",s+1); n=strlen(s+1); for(i=1;i<=n;i++) if(s[i]!='-')putchar(s[i]); else if(s[i+1]<=s[i-1])putchar('-'); else if((s[i-1]>='a'&&s[i-1]<='z'&&a 4000 mp;(s[i+1]<'a'||s[i+1]>'z'))||((s[i+1]<'0'||s[i+1]>'9')&&s[i-1]>='0'&&s[i-1]<='9')||i==1||i==n||s[i-1]=='-'||s[i+1]=='-')putchar('-'); else{ if(s[i-1]>='a'&&s[i-1]<='z')putch(); else putsz(); } return 0; }
矩阵取数游戏:
题目大意:给定一个n*m的矩阵,取m次,每次在每一行取一个数,只能在每一行的首尾位置取,第i次的得分为取的n个数的总和*2^i。求m次取数后的最大得分值。n,m<=80,aij<=1000.
题解:
unsigned long long的最大值为2^64-1,所以显然这题不管什么算法都得套一个高精度……
由秦九韶公式可知:我们从内往外取数的话就每次对于当前ans*2即可。而从内往外取数的话有一个好处:每个数都有可能为最后一个取的,然后接下来由这个数往左右扩展一个就得到了最后两次取的数,一直扩展到左右两边即可。
这样子就变成了一个经典的区间DP了。以长度为第一重循环,对于长度为len-1的最优取法我们都已经得到了,而对于一段要取的序列l,r,要么就是先取l,r-1,然后最后取r,或者先取l+1,r,最后取l,两者取个max即可。
设f[i][j]代表从l开始长度为j的数列的最大得分值,则:
f[i][j]=max(2*(f[i][j-1]+a[j]),2*(f[i+1][j-1]+a[i]))。时间复杂度:O(n*m*m*G),空间复杂度:O(m*m*G)。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m,i,j,k,ans[210],f[90][90][110],a[90],na[2][110]; void plus(int a[],int b[]){ a[0]=max(a[0],b[0]); for(int i=1;i<=a[0];i++){ a[i]+=b[i]; a[i+1]+=a[i]/10; a[i]%=10; } if(a[a[0]+1])a[0]++; while(a[a[0]]>=10){ a[a[0]+1]+=a[a[0]]/10; a[a[0]]%=10; a[0]++; } } bool cmp(int a[],int b[]){ if(a[0]==b[0]){ for(int i=a[0];i;i--) if(a[i]==b[i])continue; else return a[i]<b[i]; } return a[0]<b[0]; } void mul(int a[]){ for(int i=1;i<=a[0];i++)a[i]*=2; for(int i=1;i<=a[0];i++)a[i+1]+=a[i]/10,a[i]%=10; if(a[a[0]+1])a[0]++; while(a[a[0]]>=10){ a[a[0]+1]+=a[a[0]]/10; a[a[0]]%=10; a[0]++; } } void pplus(int a[],int b){ int i=1; a[i]+=b; while(a[i]>=10){ a[i+1]+=a[i]/10; a[i]%=10; i++; } a[0]=max(a[0],i); } int main(){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ memset(f,0,sizeof(f)); for(j=1;j<=m;j++)scanf("%d",&a[j]),f[j][1][0]=1,f[j][1][1]=2*a[j]; for(j=2;j<=m;j++){ for(k=1;k<=m-j+1;k++){ memset(na,0,sizeof(na)); plus(na[0],f[k][j-1]); pplus(na[0],a[k+j-1]); mul(na[0]); plus(na[1],f[k+1][j-1]); pplus(na[1],a[k]); mul(na[1]); if(cmp(na[0],na[1]))memcpy(f[k][j],na[1],sizeof(na[1])); else memcpy(f[k][j],na[0],sizeof(na[0])); } } plus(ans,f[1][m]); } for(i=ans[0];i;i--)printf("%d",ans[i]); return 0; }
树网的核:
题目大意:给定一棵树,一段路径记为F,D(i,F),代表i到F上最近的点的距离,一段路径的偏心距为max(D(i,F))(i<=n)。求树直径上的一段长度<=x的路径的偏心距的最小值。n<=300.
题解:
首先dfs一遍求出相邻点两两之间的距离,再用floyd求出所有点对间距离,并求出所有直径。我们需要知道这两个定理:1.一条路径上的偏心距必定是由某条直径的端点到这条路径上的距离。2.核一定是所有直径的交集的一部分。因此我们可以任取一条直径,对于该直径上的一段路径,判断是否<=s,然后求出该直径的端点与该路径的距离,更新答案即可。时间复杂度:O(n^3),空间复杂度:O(n^2)。然而这题数据太水了,怎么做都能过。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,s,i,j,k,f[310][310],x,y,z,ans=2147483647,que[310],w,maxn,maxi,maxj; int main(){ scanf("%d%d",&n,&s); memset(f,1,sizeof(f)); for(i=1;i<n;i++){ scanf("%d%d%d",&x,&y,&z); f[i][i]=0; f[x][y]=f[y][x]=z; } f =0; for(k=1;k<=n;k++) for(i=1;i<=n;i++) for(j=1;j<=n;j++) if(f[i][j]>f[i][k]+f[k][j]&&i!=j)f[i][j]=f[i][k]+f[k][j]; for(i=1;i<=n;i++) for(j=i;j<=n;j++) if(f[i][j]>maxn){ maxn=max(maxn,f[i][j]); maxi=i,maxj=j; } for(i=1;i<=n;i++) if(f[maxi][i]+f[i][maxj]==f[maxi][maxj])que[++w]=i; for(i=1;i<=w;i++) for(j=i;j<=w;j++) if(f[que[i]][que[j]]<=s)ans=min(max(min(f[maxi][que[i]],f[maxi][que[j]]),min(f[maxj][que[i]],f[maxj][que[j]])),ans); printf("%d",ans); return 0; }
相关文章推荐
- Linux之常用命令
- bzoj 1911 [Apio2010]特别行动队
- MSSQL 局部变量与全局变量
- zoj 2236 Wireless Network
- Android显示一张很长的图
- 遇见你,用尽了我一生的幸运
- 添加自定义的tabBar
- LeetCode OJ 66. Plus One
- SQL SERVER 2012 只能识别20个CPU的问题
- Yii框架表单模型和验证用法
- HQL数据查询基础(三)
- 本周任务
- 小松api文档系统终于完成
- 从另外角度-解决ASP.NET每一个页面首次访问慢的问题
- 字符串转换成json的三种方式(留存备用)
- 移动应用APP架构文档
- 2.2#####阿里云软件市场教你搭建网站(linux界面 视频)
- POJ 3449 Geometric Shapes(判断几个不同图形的相交,线段相交判断)
- Cyclic Nacklace(KMP求循环节)
- C#windows service服务