Color Length(UVA-1625)(DP LCS变形)
题目大意
输入两个长度分别为n,m(<5000)的颜色序列。要求按顺序合成同一个序列,即每次可以把一个序列开头的颜色放到新序列的尾部。
https://odzkskevi.qnssl.com/a68cbd3e27f46b4f02ea12b7b1a1abca
然后产生的新序列中,对于每一个颜色c,都有出现的位置,L(c)表示最小位置和最大位置之差,求L(c)总和最小的新序列。
分析
- LCS 是公共上升子序列,在动态转移的过程中,考虑第一个序列的 i ,和第二个序列的 j ,如果 i 和 j 相同,则
dp[i][j]=d[i-1][j-1]+1
,若不相同,则dp[i][j]= max(dp[i-1][j],dp[i][j-1])
。完成动态转移。 - 而这个题,是要让L(c)最小,在动态转移的过程中,我们要记录的值就是L(c)的和,同时还要考虑是不是有颜色开始或者结束。为了转移L(c)的和,我们还要用一个数组 C 来记录当前有多少颜色开始但尚未结束。(思路紫薯说的非常清楚)
dp[i][j] = min(dp[i-1][j]+c[i-1][j],dp[i][j-1]+c[i][j-1])
i,j 表示前 i 个和前 j 个组成新串。- 做法就是先预处理两个串中每个字符的首次出现位置和末端出现位置。然后DP。DP状态转移思路其实不难想到,但是需要有些新的细节考虑。
- 总结一些细节
- 因为在两个串的合成过程中,可以有一个串一直未参与,而另一个串在一直合成。所以DP数组中下标0就是这个含义,但是如果字符串也从下标0开始,是不是有点麻烦了?所以第一个细节就是字符串从下标1开始。
- 可以先初始化 i = 0时候的情况,但是为了整体性,放在循环中处理会更好。(预处理的只能处理第一个为0,只取第二个串的情况,但是在DP过程中,每一层都要单独处理只取第一个串,第二个串为0,所以不如直接放在循环中进行考虑)
const int maxn = 5000+5;
const int INF = 1000000000;
char p[maxn],q[maxn];
int sp[26],sq[26],ep[26],eq[26];
int d[maxn],c[maxn];//其他人博客貌似都是两层,但其实一层就够了。
int main()
{
int T;cin>>T;
while(T--)
{
scanf("%s%s",p+1,q+1);
int n = strlen(p+1),m = strlen(q+1);
for(int i=1;i<=n;i++) p[i]-='A';
for(int j=1;j<=m;j++) q[j]-='A';
for(int i=0;i<26;i++)
{
sp[i] = sq[i] = INF;
ep[i] = eq[i] = 0;
}
//get到这个预处理,以前还没遇到过。
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(c,0,sizeof c);memset(d,0,sizeof d);
for(int i=0;i<=n;i++)
{
for(int j = 0;j<=m;j++)
{
if(!j&&!i)continue;//两个都是0,就直接继续。
int v1=INF,v2 = INF;//对于这种特殊情况,只能先把两个值先寄存在v1,v2,并且赋值为INF。
if(i) v1=d[j]+c[j];
if(j) v2=d[j-1]+c[j-1];
d[j] = min(v1,v2);
if(i)
{
c[j] = c[j];
if(sp[p[i]]==i&&sq[p[i]]>j)c[j]++;//if中的条件,这里的细节也需要思考一下
if(ep[p[i]]==i&&eq[p[i]]<=j)c[j]--;
}
else if(j)
{
c[j] = c[j-1];
if(sq[q[j]] == j && sp[q[j]]>i)c[j]++;
if(eq[q[j]] == j && ep[q[j]]<=i)c[j]--;
}
}
}
printf("%d\n",d[m]);
}
return 0;
}
原文地址:https://www.cnblogs.com/1625--H/p/9751318.html
时间: 2024-11-03 15:39:26