uva 1625(dp)

代碼如下:

/*
最優子結構: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;
} 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章