今天做了两个DP的题目,只要掌握了方法,基本上都是水题。下面简单说一下:传送门(点击打开链接)
A - Prince and Princess
思路:
求最长公共子序列,只不过数据较大,不能直接求,而是将LCS问题进行转换为LIS问题,求最长上升子序列即可。
如何转化?:
最长公共子序列向最长递增子序列退化:
设有序列A,B。记序列A中各个元素在B 中的位子(降序排列),然后按在A中的位置依次列出按后求A的最长递增子序列。
例如:有A={a,b,a,c,x},B={b,a,a,b,c,a}则有a={6,3,2},b={4,1},c={5};x=/;(注意降序排列)
然后按A中次序排出{a(6,3,2),b(4,1),a(6,3,2),c(5),x()}={6,3,2,4,1,6,3,2,5};对此序列求最长递增子序列即可(转自:点击打开链接)
代码:
#include <bits/stdc++.h> using namespace std; const int M = 100000; int n,p,q; int len; int low[M],a[M],b[M],num[M]; int main() { int t; int cas = 1; scanf("%d",&t); while(t--) { scanf("%d %d %d",&n,&p,&q); memset(num,-1,sizeof(num)); for(int i=0;i<=p;i++) { scanf("%d",&a[i]); num[a[i]] = i; //LCS转换为LIS所用记录位置的数组 } int cnt = 0; int m; for(int i=0;i<=q;i++) { scanf("%d",&m); if(num[m]!=-1) { b[cnt++]=num[m]; //处理之后,直接求b的最长上升子序列即可 } } len = 0; //下面是二分找最长上升子序列 memset(low,0,sizeof(low)); low[0] = b[0]; for(int i=1;i<cnt;i++) { if(b[i]>low[len]) { low[++len] = b[i]; } else { int p = lower_bound(low,low+len,b[i])-low; low[p] = b[i]; } } printf("Case %d: %d\n",cas,len+1); cas++; } return 0; }
E - Partitioning by Palindromes
题意:给你一个串进行划分,问最少可以分成几个回文串。
思路:
暴力枚举每一部分,然后进行比较,取最小的,详见代码:
#include <bits/stdc++.h> using namespace std; int dp[1005]; char s[1005]; int main() { int t; int flag; scanf("%d",&t); while(t--) { cin>>s; int len = strlen(s); memset(dp,0x3f,sizeof(dp)); dp[0] = 1; for(int i=1; i<=len-1; i++) //以i为结尾 { for(int j=0;j<=i;j++) //以j为起点的 { flag = 1; for(int k=0;k<=(i-j)/2;k++) //每一段区间是否形成回文串 { if(s[i-k]!=s[j+k]) { flag = 0; break; } } if( flag == 1) //若形成了,在j的基础上加一操作 { if(dp[ i ] > dp [ j - 1] + 1) { dp[ i ] = dp[ j - 1 ] + 1; //找最小 } } } } printf("%d\n" , dp[len-1]); } return 0; }