8-3-COMPETITION
链接:8.3比赛
这次是动态规划里的LCS,LIS,LCIS专场.......
A.Common Subsequence
就是:给出两个字符串,求出其中的最长公共子序列的长度~LCS
代码:
1 //memory:4276KB time:109ms 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 #include<string> 6 using namespace std; 7 8 int c[1010][1010]; 9 10 int maxx(int a,int b,int c) 11 { 12 if(b>a) a=b; 13 if(c>a) a=c; 14 return a; 15 } 16 17 int LCS_Lenght(string x,string y) 18 { 19 int m=x.length(); 20 int n=y.length(); 21 int i,j,v1,v2,v3; 22 memset(c,0,sizeof(c)); 23 for(i=1;i<=m;i++) 24 for(j=1;j<=n;j++) 25 { 26 v3=c[i-1][j-1]; 27 if(x[i-1]==y[j-1]) v3=v3+1; 28 v2=c[i-1][j]; 29 v1=c[i][j-1]; 30 c[i][j]=maxx(v1,v2,v3); 31 } 32 return c[m][n]; 33 } 34 35 int main() 36 { 37 string x,y; 38 while(cin>>x>>y) 39 { 40 int p=LCS_Lenght(x,y); 41 cout<<p<<endl; 42 } 43 return 0; 44 }
B.Palindrome
题意:给出一个字符串,求出它的最长上升子序列~LIS
如果表格直接用5001x5001的会超内存(在POJ上的内存开的很大,代码能过),因此就要进行优化......因为在表格上走的每一步只与与它左侧、上侧,左斜上侧的三个点有关,每一排的点只与上一排的点有关,因此在不要求回溯求出要求点的值时,可以只用2x5001的表格即可~
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<string> 6 using namespace std; 7 8 int c[2][5002],k; 9 10 int maxx(int a,int b,int c) 11 { 12 if(b>a) a=b; 13 if(c>a) a=c; 14 return a; 15 } 16 17 int LCS_Lenght(string x) 18 { 19 int i,j,v1,v2,v3; 20 memset(c,0,sizeof(c)); 21 for(i=1;i<=k;i++) 22 for(j=1;j<=k;j++) 23 { 24 v3=c[(i-1)%2][j-1]; //对二取余是重点~ 25 if(x[i-1]==x[k-j]) v3=v3+1; 26 v2=c[(i-1)%2][j]; 27 v1=c[i%2][j-1]; 28 c[i%2][j]=maxx(v1,v2,v3); 29 } 30 return c[k%2][k]; 31 } 32 33 int main() 34 { 35 string x; 36 while(cin>>k) 37 { 38 cin>>x; 39 int p=LCS_Lenght(x); 40 cout<<k-p<<endl; 41 } 42 return 0; 43 }
//memory:344KB time:687ms
C.魔法串
给出两个字符串a和b,并给出一些转换关系,看b是否能通过这些转换关系和删除字符得到a~
代码:
//打出一个转换表,看b中的字符是否能通过转换关系与a中的字符相等~
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 using namespace std; 5 6 const int N = 1005; 7 8 int main() 9 { 10 int cas,t; 11 scanf("%d",&t); 12 for(cas = 1; cas<=t; cas++) 13 { 14 getchar(); 15 char s1[N],s2[N]; 16 int len1,len2; 17 gets(s1); 18 gets(s2); 19 len1 = strlen(s1); 20 len2 = strlen(s2); 21 int n,i,j; 22 int change[30][30] = {0}; 23 scanf("%d",&n); 24 for(i = 0; i<n; i++) 25 { 26 char a,b; 27 getchar(); 28 scanf("%c %c",&a,&b); 29 change[a-'a'][b-'a'] = 1;//打下一个转换表 30 } 31 j = 0; 32 int flag = 0; 33 for(i = 0; i<len1; i++) 34 { 35 if(j == len2) 36 break; 37 if(s1[i] == s2[j]) //相等继续 38 { 39 j++; 40 continue; 41 } 42 while(s2[j]!=s1[i]) 43 { 44 if(j==len2) 45 { 46 flag = 1; 47 break; 48 } 49 if(change[s2[j]-'a'][s1[i]-'a'] == 1) 50 { 51 j++; 52 break; 53 } 54 else 55 j++; 56 } 57 } 58 printf("Case #%d: ",cas); 59 if(!flag) 60 printf("happy\n"); 61 else 62 printf("unhappy\n"); 63 } 64 return 0; 65 }
//memory:232KB time:0ms
代码来自:http://blog.csdn.net/libin56842/article/details/8960511
方法二:
找最长公共子序列(LCS),在对应关系中略有改动~
代码:
1 #include<iostream> 2 #include<string> 3 #include<cstring> 4 using namespace std; 5 int dp[1005][1005]; 6 bool has[128][128]; 7 int maxi(int x,int y) 8 { 9 if(x>y) 10 return x; 11 else return y; 12 } 13 int main() 14 { 15 int i,j,t,m,count=0,len1,len2; 16 char a,b; 17 string str1,str2; 18 cin>>t; 19 while(t--) 20 { 21 count=count+1; 22 23 cin>>str1>>str2; 24 len1=str1.length(); 25 len2=str2.length(); 26 memset(dp,0,sizeof(dp)); 27 memset(has,0,sizeof(has)); 28 cin>>m; 29 for(j=1;j<=m;j++) 30 { 31 cin>>a>>b; 32 has[a][b]=1; //记录对应关系~ 33 } 34 cout<<"Case #"<<count<<": "; 35 for(i=1;i<=len1;i++) 36 for(j=1;j<=len2;j++) 37 { 38 if(str1[i-1]==str2[j-1]||has[str2[j-1]][str1[i-1]]==1) //has[][]是看通过对应关系能否相等~ 39 dp[i][j]=dp[i-1][j-1]+1; 40 else dp[i][j]=maxi(dp[i-1][j],dp[i][j-1]); 41 } 42 43 if(dp[len1][len2]==len1) 44 cout<<"happy"<<endl; 45 else cout<<"unhappy"<<endl; 46 } 47 return 0; 48 }
//memory:4264KB time:78ms
D.最少拦截系统
贪心算法:
找了段便于理解的解释就直接贴出来了。对于这个题目的测试例的解释:当8枚导弹依次飞来时,对于前三枚导弹(300,207,155),用第一套系统即可拦截,每一次更新记录,即:对第一枚导弹直接拦截,第二枚导弹来临时也用第一套系统拦截,记录改变成207,当用第一套系统拦截第三枚后记录修改为155,当第四枚导弹来时按照系统规定,第一套系统无法实现对第四枚导弹的拦截任务,因此,开第二套系统,为了使用最少系统,必须对以后的每一枚导弹贪心,即:拿这枚导弹和以前的导弹最小记录依次做比较,寻找一个和这枚导弹高度最接近的记录并用这枚导弹的高度替换原记录,最后记录数组中的个数就是最少需要的系统数。
代码:
#include<stdio.h> #include<math.h> int a[30001],b[30001]; int main() { int n,i,j,k; while(scanf("%d",&n)!=EOF) { b[0]=0;k=0; for(i=0;i<n;i++) { scanf("%d",&a[i]); for(j=0;j<=k;j++) { if(a[i]<b[j]) { b[j]=a[i]; break; } else if(j==k) { k++; b[k]=a[i]; break; } } } printf("%d\n",k); } return 0; }
//memory:240KB time:15ms
动态规划:
代码:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int main() { int t,a[1001],dp[1001],i,j,max; while(scanf("%d",&t)!=EOF) { for(i=1;i<=t;i++) scanf("%d",&a[i]); memset(dp,0,sizeof(dp)); max=-1; for(i=1;i<=t;i++) for(j=i-1;j>=0;j--) if(a[i]>a[j] && dp[i]<dp[j]+1) //如果拦截中出现了非单调递减的高度~拦截系统就加1~ dp[i]=dp[j]+1; for(i=1;i<=t;i++) if(max<dp[i]) max=dp[i]; printf("%d\n",max); } return 0; }
//memory:232KB time:15ms
方法三:
通过对E题的思考,突然发现其实D题的本质与E题相同,同样可以用E题的方法一解出来~然后经过与同学的讨论......额~无论是求最长上升子序列还是最长下降子序列都可以用此方法(略微做一点变形)做出来~~~
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 7 class Dolls 8 { 9 public: 10 int x,id; 11 }D[20000]; 12 13 int main() 14 { 15 int n; 16 while(~scanf("%d",&n)) 17 { 18 int i,minn; 19 memset(D,0,sizeof(D)); 20 for(i=0;i<n;i++) 21 scanf("%d",&D[i].x); 22 int number=0,j; 23 for(i=0;i<n;i++) 24 if(D[i].id==0) 25 { 26 minn=D[i].x; 27 number++; 28 for(j=i+1;j<n;j++) 29 if(D[j].x<minn && D[j].id==0) 30 { 31 D[j].id=1; 32 minn=D[j].x; 33 } 34 } 35 printf("%d\n",number); 36 } 37 return 0; 38 }
E.Nested Dolls
题意:有一堆俄罗斯套娃,把这些套娃全部套好,看最后会剩多少套娃~
方法一:
把所有套娃都按x(宽度)从大到小排好序【注:当宽度相等的时候,y(高度)小的排在前】,然后从第一个套娃开始看排在后的套娃能放进多少,能放进的都进行标记,并更新minn(能放进套娃的最大高度);再找下一个未标记的套娃,重复操作,......直到没有未标记的套娃为止~操作了都少次,就剩下多少套娃~
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 7 class Dolls 8 { 9 public: 10 int x,y,id; 11 }D[20000]; 12 13 bool comp(Dolls a,Dolls b) //对套娃进行排序 14 { 15 if(a.x==b.x) return a.y<b.y; 16 return a.x>b.x; 17 } 18 19 int main() 20 { 21 int t,n; 22 scanf("%d",&t); 23 while(t--) 24 { 25 int i,minn; 26 scanf("%d",&n); 27 memset(D,0,sizeof(D)); 28 for(i=0;i<n;i++) 29 scanf("%d%d",&D[i].x,&D[i].y); 30 sort(D,D+n,comp); 31 int number=0,j; 32 for(i=0;i<n;i++) 33 if(D[i].id==0) //找到未标记的套娃(即作为容器的最大套娃) 34 { 35 minn=D[i].y; 36 number++; 37 for(j=i+1;j<n;j++) 38 if(D[j].y<minn && D[j].id==0) //判断是否能够放进这个套娃 39 { 40 D[j].id=1; //能放进进行标记 41 minn=D[j].y; //更新能放进套娃的最大高度 42 } 43 } 44 printf("%d\n",number); 45 } 46 return 0; 47 }
//memory: 464KB time: 468ms
F.Greatest Common Increasing Subsequence
题意:看题目就知道,是求最长公共上升子序列~~~~
//题目有个略坑的地方,输出要空一行,最后一个输出又不要空.........不这样的话会PE......= =
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 int main() 7 { 8 long t,n,m,a[501],b[501],len[501],i,j,k,maxx; 9 scanf("%ld",&t); 10 while(t--) 11 { 12 scanf("%ld",&n); 13 for(i=1;i<=n;i++) 14 scanf("%d",&a[i]); 15 scanf("%ld",&m); 16 for(i=1;i<=m;i++) 17 scanf("%ld",&b[i]); 18 memset(len,0,sizeof(len)); 19 for(i=1;i<=n;i++) 20 { 21 k=0; 22 for(j=1;j<=m;j++) 23 { 24 if(a[i]>b[j] && len[j]>len[k]) //len[j]代表的是b[j]处,得到的最长公共上升子序列的长度 25 k=j; //len[k]代表的是在a[i]>b[j]时,所有的上升子序列中最长的一个~ 26 if(a[i]==b[j]) 27 len[j]=len[k]+1; 28 } 29 } 30 for(i=0,maxx=0;i<=m;i++) 31 if(len[i]>maxx) maxx=len[i]; //找出最长公共上升子序列的长度~~ 32 printf("%ld\n",maxx); 33 if(t) printf("\n"); 34 } 35 return 0; 36 }
//memory:228KB time:0ms
G.吉哥系列故事――完美队形I
与F题其实很相似,代码原理上是相同的,只是略有改动~
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 using namespace std; 6 7 int main() 8 { 9 int t,n,a[220],b[220]; 10 scanf("%d",&t); 11 while(t--) 12 { 13 int mxx,number=0,i,j; 14 scanf("%d",&n); 15 memset(a,0,sizeof(a)); 16 for(i=0;i<n;i++) 17 scanf("%d",&a[i]); 18 memset(b,0,sizeof(b)); 19 for(i=n-1;i>=0;i--) 20 { 21 mxx=0; 22 for(j=0;j<=i;j++) 23 { 24 if(a[j]<a[i]) 25 mxx=mxx>b[j]?mxx:b[j]; //mxx代表在a[j]<a[i]时最大的上升子序列的对数 26 else 27 if(a[j]==a[i]) 28 b[j]=mxx+1; 29 if(j<i && number<2*b[j]) number=2*b[j]; 30 else 31 number=number>2*b[j]-1?number:2*b[j]-1; 32 } 33 } 34 printf("%d\n",number); 35 } 36 return 0; 37 }
//memory:228KB time:0ms