今天做了两个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;
}