您的位置:首页 > 其它

【DP模型:LCS】uva1625 Color Length

2015-07-22 09:00 387 查看
题目描述:对给定的两个长度为n和m的颜色序列,将他们合并,即每次把序列的开头放入新的序列的尾部。计每种颜色的跨度为序列中最大位置与最小位置之差。求最小跨度。

数据范围:n,m<=5000

这道题很容易想到LCS(最长公共子序列)。然而并没有什么用= =

一开始我想的状态转移方程式:

f[i][j]=max(f[i-1][j]+add(a[i]),f[i][j-1]+add(b[j])) //a,b为两个颜色序列 add表示放入某元素增加的跨度

发现add()并不好算= =

看大神的方法:采用倒推。

令f(i,j)表示两个串的下标移动至i,j时,还要产生的最小跨度。

方程式:f(i,j)=min(f(i,j+1),f(i+1,j))+add(i,j)

初始化:f(lena,lenb)=0

其中add(i,j)怎么算?

计b1[i]为i在第一个序列开始位置,b2[i]为i在第二个序列开始位置

e1[i]为i在第一个序列结束位置,e2[i]为i在第二个序列结束位置

若i已经出现完了,就不再增加跨度。若i还未出现,也不再增加跨度。其余情况均增加跨度。(具体操作见代码)

目标:f(0,0)

在实现过程中注意细节。

本题思维难度适中。然而我并没有想出来= = 说明逆向思维不够= =

蒟蒻加油 ↖(^ω^)↗

#include <iostream>
#include <cstdio>
#include <cstring>
#define min(a,b) ((a)<(b)?(a):(b))
#define MAXN 5005
using namespace std;

int len1 ,len2 ,b1[27] ,b2[27] ,e1[27] ,e2[27] ;
int f[MAXN][MAXN] ;
char c1[MAXN] ,c2[MAXN] ;

int add(int a,int b)
{
--a,--b;
int cnt=0;
for(int i=0;i<26;++i)
{
if(e1[i]<=a&&e2[i]<=b)continue;
if(b1[i]>a&&b2[i]>b||b1[i]>a&&b2[i]==-1||b2[i]>b&&b1[i]==-1)continue;
++cnt;
}
return cnt;
}

int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(b1,-1,sizeof(b1));
memset(b2,-1,sizeof(b2));
memset(e1,-1,sizeof(e1));
memset(e2,-1,sizeof(e2));

scanf("%s%s",c1,c2);
len1=strlen(c1),len2=strlen(c2);

for(int i=0;i<len1;++i)
{
c1[i]-='A';
if(b1[c1[i]]==-1)
b1[c1[i]]=i;
e1[c1[i]]=i;
}
for(int i=0;i<len2;++i)
{
c2[i]-='A';
if(b2[c2[i]]==-1)
b2[c2[i]]=i;
e2[c2[i]]=i;
}

f[len1][len2]=0;
for(int i=len2-1;i>=0;--i)
f[len1][i]=f[len1][i+1]+add(len1,i);
for(int i=len1-1;i>=0;--i)
{
f[i][len2]=f[i+1][len2]+add(i,len2);
for(int j=len2-1;j>=0;--j)
f[i][j]=min(f[i][j+1],f[i+1][j])+add(i,j);
}
printf("%d\n",f[0][0]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: