zoukankan      html  css  js  c++  java
  • 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
    ",f[n][m]);
        }
        return 0;
    }
    
  • 相关阅读:
    luogu4781
    luogu 4933
    luogu p1726
    bzoj2238
    luogu 1462 通往奥格瑞玛的道路
    noip.ac 3276 矩阵
    luogu1144
    noip.ac 3248
    奶牛比赛
    小P的Civilization V
  • 原文地址:https://www.cnblogs.com/HotPants/p/13476532.html
Copyright © 2011-2022 走看看