uva1625 题解

题目链接:uva 1625 Color Length

题目大意

给定了两个长度分别为n和m的字符串,只包含有大写字母.现在要合并两个字符串成一个,具体操作是依次从两个字符串里选取开头的字符,放入正在构造的字符串的末尾.给最终得到的字符串一个权值,计算方式是里面所有元素的第一个与最后一个的位置之差的总和.求所有方案中的最小值.

数据范围:
\(1 \leq n ,m \leq 5000\)

思路

从复杂度上来推断,大概是一个\(O(nm)\)的复杂度.那么由于每次插入一个元素到末尾这件事是跟前面无关的,所以比较直接的想法就是\(dp\)来做.状态:\(f[i][j]\)表示当前字符串A里用了1i这些字符,B里用了1j,所有的构造方案里当前权值之和的最小值是多少.但是这个状态没有保存第一个和最后一个字符具体在哪,这导致转移非常的困难.因为根本不能从这个状态里推断说当前这个字符具体有没有用,是不是最后一个.

梳理一下当前这个问题:这个状态只能知道"宏观"上用了哪一部分的字符,而不能知道具体用了谁.而每个元素的权的计算是跟具体在哪被使用有关的.不妨思考这样一个转换:有没有什么办法让单个元素的权值之和的计算变成一个总体的"宏观"的计算呢?

这个转换就是不直接计算每个元素具体要不要去增加权值,而是计算增加一个元素之后,对所有的和会有怎样的影响.具体来说,如果插入的一个新元素是s的话,当前这个s是A里的第一个,而且当前枚举的B里的位置还没走到第一个s,那么就要让权值增加一.如果当前这个s是A里的最后一个,并且当前枚举的B里的位置已经走到最后一个了,那么就要让权值减少一.这样维护的具体含义就是当这个元素放入序列的时候,所有已经出现过,但是还没走到最后一个的这些元素他们的权值都要增加一,因为你每加一个,这些已经出现过但没走到最后一位的,他们跟最后一位的距离就增加了一.

那么想到这个维护的思路之后,就可以发现整个题大概就做出来了.但是还要结合一下DP的具体过程来思考怎么写.下面说一下DP的设计部分.

状态设计

状态定义:\(f[i][j]\)表示当前用了A里的1i部分的元素,B里用了1j部分的元素,所有构造方案里权值之和最小的是多少.
辅助的状态:\(cost[i][j]\)同样表示用了哪些元素,产生的最小的权值之和是多少.
入口:\(f[0][0] = cost[0][0] = 0\) 其他元素均为正无穷
转移:
①当\(i >1\)时,可以用A[i]转移过来.上一个状态是\(f[i - 1][j]\)以及\(cost[i - 1][j]\)
转移方程:\(f[i][j] = min(f[i][j],f[i - 1][j] + cost[i - 1][j]\)
同时在这一位还要计算一下\(cost[i][j]\).
假如当前这个新加入的元素是s,并且s是第一次出现在A里,并且s还没有在B里出现,那么就要对\(cost[i][j]\)增加一个一.如果s是最后一次出现在A里,而且在B里的最后一个位置已经超过了那么说明s这个元素后面就不可能再出现了,让\(cost[i][j]\)减少一就可以了.
②当\(j>1\)时,可以用B[j]转移过来,上一个状态是\(f[i][j - 1]\)以及\(cost[i][j - 1]\)那么同上镜像的写出方程以及维护\(cost[i][j]\)的方程就比较简单了.
转移方程:\(f[i][j] = min(f[i][j],f[i][j - 1] + cost[i][j - 1]\)
如果当前元素s是B里第一次出现,并且在A里没有出现过,那么就给\(cost[i][j]\)增加一.如果是最后一次出现在B里,并且已经跨过了A里的最后一个位置,那么就给\(cost[i][j]\)减少一.
出口:\(f[n][m]\)
预处理的时候顺便求一下第一次以及最后一次出现的位置就可以了.
注意实现的时候,第一个出现的位置设置成正无穷,最后一个设置成-1.这个是有原因的,因为可能出现说有A里出现的元素s在B里不存在,这个时候对应进去两种情况必须要直接让他不满足,可以结合代码理解一下,当然特判也可以.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5005;
char A[N],B[N];
int f[N][N],cost[N][N],occ[2][N][2];
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
		scanf("%s",A + 1);getchar();
		scanf("%s",B + 1);getchar();
		int n = strlen(A + 1),m = strlen(B + 1);
		//occ
		for(int i = 0;i <= 25;++i)	
		{
			occ[0][i][0] = occ[1][i][0] = 0x3f3f3f3f;
			occ[0][i][1] = occ[1][i][1] = -1;
		}
		for(int i = 1;i <= n;++i)
		{
			if(occ[0][A[i] - 'A'][0] == 0x3f3f3f3f)	
				occ[0][A[i] - 'A'][0] = occ[0][A[i] - 'A'][1] = i;
			else occ[0][A[i] - 'A'][1] = i;
		}
		for(int i = 1;i <= m;++i)
		{
			if(occ[1][B[i] - 'A'][0] == 0x3f3f3f3f)	
				occ[1][B[i] - 'A'][0] = occ[1][B[i] - 'A'][1] = i;
			else occ[1][B[i] - 'A'][1] = i;
		}
		
		f[0][0] = 0;
		cost[0][0] = 0;
		for(int i = 0;i <= n;++i)
		{
			for(int j = 0;j <= m;++j)
			{
				if(i == 0 && j == 0)	continue;
				f[i][j] = 0x3f3f3f3f;
				if(i > 0)
				{
					f[i][j] = min(f[i][j],f[i - 1][j] + cost[i - 1][j]);
					cost[i][j] = cost[i - 1][j];
					int ap = A[i] - 'A';
					int fA = occ[0][ap][0],fB = occ[1][ap][0];
					if(i == fA && j < fB)	++cost[i][j];
					int lA = occ[0][ap][1],lB = occ[1][ap][1];
					if(i == lA && j >= lB)	--cost[i][j];
				}
				if(j > 0)
				{
					f[i][j] = min(f[i][j],f[i][j - 1] + cost[i][j - 1]);
					cost[i][j] = cost[i][j - 1];
					int ap = B[j] - 'A';
					int fA = occ[0][ap][0],fB = occ[1][ap][0];
					if(i < fA && j == fB)	++cost[i][j];
					int lA = occ[0][ap][1],lB = occ[1][ap][1];
					if(i >= lA && j == lB)	--cost[i][j];
				}
			}
		}
		printf("%d\n",f[n][m]);
    }
    return 0;
}
posted @ 2020-08-11 16:37  随处可见的阿宅  阅读(210)  评论(0编辑  收藏  举报