uva 1625(dp)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39562952/article/details/85094846

代码如下:

/*
最优子结构:dp[i][j](1...i,1...j)合并后的minsum
子问题:dp[i][j-1],dp[i-1,j](合并最后一个字符是来自哪个字符串)
证明:最优解的最后一个元素必定来自两个字符串的末尾,如果子问题不包含最优解,那么说明dp[i][j-1],dp[i-1][j]不是最优解(矛盾)
具体实现:滚动数组记录dp[i][j],并且记录这个状态下每个字符的最后出现位置,之后计算时,别的元素都不变,只边最后位置
上面的办法不行,因为没办法只通过字符最后的位置,算出字符的L(c),(因为开始位置不知道)
标程上的办法就很好,用c[i][j]记录在(1...i,1...j)中已经出现的字符并且还没有结束的个数,就是增量,并且用滚动数组减少内存
同时预处理出字符一开始出现的位置和最后出现的位置 
*/
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 5100
#define inf  1000000
int  dp[2][maxn];
int  c[2][maxn]; 
char s1[maxn];
char s2[maxn];
int sp[26],ep[26],sq[26],eq[26];
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%s%s",s1+1,s2+1);
		int n=strlen(s1+1);
		int m=strlen(s2+1);
		for(int i=0;i<26;i++)
		{
			sp[i]=maxn,sq[i]=maxn,ep[i]=0,eq[i]=0;
		}
		for(int i=1;i<=n;i++)
		{
			s1[i]-='A';
			if(sp[s1[i]]==maxn)
			{
				sp[s1[i]]=i;
			}
			ep[s1[i]]=i;
		}
		for(int i=1;i<=m;i++)
		{
			s2[i]-='A';
			if(sq[s2[i]]==maxn)
			{
				sq[s2[i]]=i;
			}
			eq[s2[i]]=i;
		}
		/*for(int i=0;i<26;i++)
		{
			printf("sp %c:%d,ep %c:%d,sq %c:%d,eq %c:%d\n",i+'A',sp[i],i+'A',ep[i],i+'A',sq[i],i+'A',eq[i]);
		}*/
		int t=0;
		memset(dp,0,sizeof(dp));
		memset(c,0,sizeof(c));
		for(int i=0;i<=n;i++)
		{
			for(int j=0;j<=m;j++)
			{
				if(!i&&!j)
					continue;
				int v1=inf;
				int v2=inf;
				if(i) v1=dp[t^1][j]+c[t^1][j];
				if(j) v2=dp[t][j-1]+c[t][j-1];
				dp[t][j]=min(v1,v2);
				if(i)
				{
					c[t][j]=c[t^1][j];
					if(sp[s1[i]]==i&&sq[s1[i]]>j) c[t][j]++;//新出现
					if(ep[s1[i]]==i&&eq[s1[i]]<=j) c[t][j]--;//结束 
				} 
				else if(j)
				{
					c[t][j]=c[t][j-1];
					if(sq[s2[j]]==j&&sp[s2[j]]>i) c[t][j]++;
					if(eq[s2[j]]==j&&ep[s2[j]]<=i) c[t][j]--;
				}
			//	printf("%d ",dp[t][j]);
			} 
			//printf("\n");
			t^=1;
		}
		printf("%d\n",dp[t^1][m]);
	}	
	
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/qq_39562952/article/details/85094846