您的位置:首页 > 其它

UVA-1625 Color Length

2015-11-30 15:29 399 查看
Brief description

这里直接copy紫书上面的文字了。

输入2个长度为n和m(n,m<=5000)的颜色序列,要求按照顺序合并成同一个序列,即每次可以把一个序列的开头的颜色放到新序列的尾部。

例如,两个颜色序列GBBY和YRRGB,至少有2种合并结果:GBYBRYBGB和YRRGGBBYB。对于每个颜色c来说,其跨度L(c)等于最大位置与最小位置之差。

你的任务是找一种合并方式,使得所有的L(c)的总和最小。

Algorithm analyse

在没有看题解之前不难想到一个大致的状态转移方程dp[i][j]=min(dp[i-1][j]+m,dp[i][j-1]+k),但是m,k在状态转移时候的加入字母导致的一些变化量无从下手。还有一些临界条件的分析很混乱。所以很仔细地看了刘汝佳老师的代码.

Code

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn=5050;
const int INF = 1000000000;

char p[maxn],q[maxn];
int sp[26],sq[26],ep[26],eq[26];
int d[2][maxn],c[2][maxn];

int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s%s",p+1,q+1);
int n=strlen(p+1);
int m=strlen(q+1);
for(int i=1;i<=n;i++)  p[i]-='A';
for(int i=1;i<=m;i++)  q[i]-='A';
for(int i=0;i<26;i++)
{
sp[i]=sq[i]=INF;
ep[i]=eq[i]=0;
}
for(int i=1;i<=n;i++)
{
sp[p[i]]=min(sp[p[i]],i);
ep[p[i]]=i;
}
for(int i=1;i<=m;i++)
{
sq[q[i]]=min(sq[q[i]],i);
eq[q[i]]=i;
}
memset(d,0,sizeof(d));
memset(c,0,sizeof(c));
int t=0;
for(int i=0;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
if(!i&&!j) continue;
int v1,v2;
v1=v2=INF;
if(i)  v1=d[t^1][j]+c[t^1][j];
if(j)	 v2=d[t][j-1]+c[t][j-1];
d[t][j]=min(v1,v2);
if(i)
{
c[t][j]=c[t^1][j];
if(sp[p[i]]==i&&sq[p[i]]>j)  c[t][j]++;
if(ep[p[i]]==i&&eq[p[i]]<=j)   c[t][j]--;
}
else if(j)
{
c[t][j]=c[t][j-1];
if(sq[q[j]]==j&&sp[q[j]]>i)  c[t][j]++;
if(eq[q[j]]==j&&ep[q[j]]<=i)   c[t][j]--;
}
}
t^=1;
}
printf("%d\n",d[t^1][m]);
}
return 0;
}


Summary

1.掌握记录每个颜色在两个序列的中的开始和结束位置的方法.

2.对于临界条件的把握.这里有两点 a. i,j分别为0的时候的讨论

b. 开始与结束的判断.例如在代码

if(sp[p[i]]==i&&sq[p[i]]>j)  c[t][j]++;
if(ep[p[i]]==i&&eq[p[i]]<=j) c[t][j]++;


为何不是sq>=然后再eq<?

对于每个颜色,一定要是最先插入,最后结束

j表示当前第2列放入了j个颜色. sq==的话,表示这个颜色已经有,eq==的话,表示这个颜色还没结束.

3.滚动数组:

目的是节约空间消耗.当当前的变化只与上一个相关时,可以这么使用。看这个简便理解。

/article/2390545.html

点击打开链接
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: