【NOIP2008】提高组
T1笨小猴
开个桶统计,扫一遍记录min,max再O(sqrt(n))判合法就行了。
1 #include<cstdio> 2 #include<cmath> 3 #include<cstring> 4 #include<algorithm> 5 int ton[27],mni,mxa; 6 char ch[105]; 7 bool check(int x){ 8 if(x<2)return 0; 9 for(int i=2;i<=sqrt(x);i++) 10 if(x%i==0)return 0; 11 return 1; 12 } 13 int main(){ 14 scanf("%s",ch+1); 15 int len=strlen(ch+1); 16 for(int i=1;i<=len;i++)ton[ch[i]-'a'+1]++; 17 mni=len;mxa=0; 18 for(int i=1;i<=26;i++){ 19 if(!ton[i])continue; 20 mni=std::min(ton[i],mni); 21 mxa=std::max(ton[i],mxa); 22 } 23 int p=mxa-mni; 24 if(check(p)) 25 printf("Lucky Word\n%d",p); 26 else printf("No Answer\n0"); 27 return 0; 28 }
T2火柴棒等式
n<=24直接上剪枝DFS即可。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 int n,sum=0,p; 5 int count[10]={6,2,5,5,4,5,6,3,7,6}; 6 void dfs(int cc,int x,int k){ 7 int x0=x,num=0; 8 if(!x0)num=6; 9 while(x0){ 10 num+=count[x0%10];x0/=10; 11 } 12 if(num==k&&cc==3){sum++;return;} 13 if(num>=k||cc==3)return; 14 if(cc==1)for(int i=0;i<=1000;i++)dfs(2,i,k-num); 15 else dfs(3,p+x,k-num); 16 } 17 int main(){ 18 scanf("%d",&n); 19 n-=4; 20 for(p=0;p<=1000;p++)dfs(1,p,n); 21 printf("%d",sum); 22 return 0; 23 }
T3传纸条
看成是从左上角找两条除左上右下之外不相交的路径,
那么就可以用f[i][j][x][y]表示走到(i,j)和(x,y)两个点的最大值。
因为i,j,x,y知道其中三个就可以算出最后一个,因此数组只要开三维就够了。
转移O(n3)。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 const int N=55; 5 int n,m; 6 int read(){ 7 int ans=0,f=1;char c=getchar(); 8 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 9 while(c>='0'&&c<='9'){ans=ans*10+c-48;c=getchar();} 10 return ans*f; 11 } 12 int min(int x,int y){return x>y?y:x;} 13 int max(int x,int y){return x<y?y:x;} 14 int mp[N][N]; 15 int f[N][N][N]; 16 int main(){ 17 m=read();n=read(); 18 for(int i=1;i<=m;i++) 19 for(int j=1;j<=n;j++)mp[i][j]=read(); 20 for(int i=1;i<=m;i++){ 21 for(int j=1;j<=n;j++){ 22 if(i==1&&j==1)continue; 23 for(int x=1;x<=min(i+j,m);x++){ 24 if(x==i&&(i!=m||j!=n))continue; 25 int y=i+j-x; 26 int&p=f[i][j][x]; 27 p=f[i-1][j][x]; 28 p=max(p,f[i-1][j][x-1]); 29 p=max(p,f[i][j-1][x]); 30 p=max(p,f[i][j-1][x-1]); 31 p+=mp[i][j]+mp[x][y]; 32 } 33 } 34 } 35 printf("%d",f[m][n][m]); 36 return 0; 37 }
T4双栈排序
这道题的思想其实在后来的关押罪犯中有再次体现,说明历史还是会重复的=)
用i和i+n表示对立的两个集合,每次要入栈时就枚举一下比它小的数在哪个栈里,尝试加入到另一个栈里,如果发现冲突就说明无解。
因为要按选字典序最小的方案,所以能塞到第一个栈就塞进去。
还有就是枚举完注意可能栈里还有元素因此要让站内元素出栈。
然后,没了......
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define mem(a,p) memset(a,p,sizeof(a)) 5 const int N=1005; 6 int read(){ 7 int an=0,f=1;char c=getchar(); 8 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 9 while(c>='0'&&c<='9'){an=an*10+c-48;c=getchar();} 10 return an*f; 11 } 12 int n,mni,st[N*2],tot=0,fa[N*2],a[N]; 13 char an[N*2]; 14 bool ok[N*2]; 15 int getf(int x){return x==fa[x]?x:fa[x]=getf(fa[x]);} 16 int main(){ 17 n=read();mni=1; 18 for(int i=1;i<=n;i++)a[i]=read(),fa[i]=i,fa[i+n]=i+n; 19 for(int i=1;i<=n;i++){ 20 while(mni<=n&&ok[mni]==1)ok[mni]=0,mni++; 21 for(int j=1;j<a[i];j++){ 22 if(!ok[j])continue; 23 int f1=getf(j),f2=getf(j+n); 24 if(f1==a[i]||f2==a[i]+n)return printf("0\n"),0; 25 fa[f1]=a[i]+n;fa[f2]=a[i]; 26 } 27 ok[a[i]]=1; 28 } 29 mem(ok,0);mni=1; 30 for(int i=1;i<=n;i++){ 31 while(mni<=n&&ok[mni])an[++tot]='a'+st[getf(mni)],mni++; 32 int f1=getf(a[i]),f2=getf(a[i]+n); 33 if(!st[f1])st[f1]=1,st[f2]=3; 34 an[++tot]='a'+st[f1]-1;ok[a[i]]=1; 35 } 36 while(mni<=n&&ok[mni])an[++tot]='a'+st[getf(mni)],mni++;//这里别完了剩余元素出栈 37 for(int i=1;i<tot;i++)printf("%c ",an[i]);printf("%c",an[tot]); 38 return 0; 39 }