UVA - 1625 Color Length(线性dp)

orz好久不写博客了,主要是觉着有的题补上了就补上了没有写博客的必要……

dp还是一如既往地菜,大概是不会这种一步步拆贡献的dp,像极了不会把学习进程拆成一天天努力的我……

题目

T组样例,T未知

每组样例给两个仅大写字母构成的串,每个串长不超过5000,

现在要你把这两个串归并成一个新串,

新串中,每种字母对答案的贡献是其最后出现的位置-最开始出现的位置

求新串的最小代价和

思路来源

https://github.com/aoapc-book/aoapc-bac2nd/blob/master/ch9/UVa1625.cpp

题解

dp[i][j]代表第一个串到i第二个串到j的最小代价和

add[i][j]代表第一个串到i第二个串到j的时候,还有多少种字符对答案有贡献

对答案有贡献,即出现了开头,但还没出现结尾,

枚举这一次填i还是填j,显然从dp[i-1][j]+add[i-1][j]和dp[i][j-1]+add[i][j-1]转移而来

不管怎么算,add[i][j]都是一样的,与顺序无关

因为字符X有贡献的话,只有[i+1,n]∪[j+1,m]中出现X才可,这与[1,i][1,j]怎么合并顺序无关

5000的串长,dp[5000][5000]开不下,所以滚动数组

代码

#include<bits/stdc++.h>
using namespace std;
const int N=5e3+10,INF=0x3f3f3f3f;
int t,n,m;
char s[2][N];
int dp[N][N],add[N][N];
int las,now,v,x,y,len[2],st[2][26],ed[2][26];
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		for(int i=0;i<2;++i)
		{
			scanf("%s",s[i]+1);
			len[i]=strlen(s[i]+1);
			for(int j=0;j<26;++j)
			{
				st[i][j]=len[i]+1;
				ed[i][j]=0;
			}
			for(int j=1;j<=len[i];++j)
			{
				v=s[i][j]-'A';
				ed[i][v]=j;
			}
			for(int j=len[i];j>=1;--j)
			{
				v=s[i][j]-'A';
				st[i][v]=j;
			}	
		}
		now=0;las=1;
		dp[now][0]=add[now][0]=0;
		dp[las][0]=add[las][0]=0;
		for(int i=0;i<=len[0];++i)
		{
			for(int j=0;j<=len[1];++j)
			{
				if(!i&&!j)continue;
				x=y=INF;
				if(i)x=dp[las][j]+add[las][j];
				if(j)y=dp[now][j-1]+add[now][j-1];
				dp[now][j]=min(x,y);
				if(i)
				{
					add[now][j]=add[las][j];
					v=s[0][i]-'A';
					if(st[0][v]==i&&st[1][v]>j)add[now][j]++;
					if(ed[0][v]==i&&ed[1][v]<=j)add[now][j]--;
				}
				else if(j)
				{
					add[now][j]=add[now][j-1];
					v=s[1][j]-'A';
					if(st[1][v]==j&&st[0][v]>i)add[now][j]++;
					if(ed[1][v]==j&&ed[0][v]<=i)add[now][j]--;
				} 
			}
			swap(now,las);
		}
		printf("%d\n",dp[las][len[1]]);
	}
	return 0;
}
发布了467 篇原创文章 · 获赞 53 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/Code92007/article/details/104081693