Uva1625 -Color Length(DP)

来源:互联网 发布:我知主掌管明天 编辑:程序博客网 时间:2024/03/28 18:04

Uva1625 -Color Length

题意:有两个颜色序列(由大写字母表示),每次可以从两个序列中取出第一个颜色,重复此操作将两个序列合并,定义lc为颜色c在合并后的序列中最后出现的位置与最开始出现的位置的差,求使得所有颜色的lc之和最小的值。

 

分析:很明显的无后效性,考虑dp,这题的难点就在于怎么进行规划。首先定义dp[i][j]为从第一个颜色序列中拿走前i个颜色,从第二个颜色中拿走前j个颜色时合并产生的序列的最小lc之和,根据题意那么我们需要在转移的过程中需要知道一种颜色最开始的位置与最后的位置,显然从状态中无法得知,那么怎么办呢,不可能在状态中记录这些信息,空间时间无法承受,那么怎么办呢?转换一种思路,还是很巧妙的,做的时候真的没想到。从计算方式上考虑,如果一个颜色已经移到序列中,不要等到一个颜色全部移完才计算,而是只要这个颜色没有全部移动到最终的序列,那么lc就要加1,只不过计算方式是不断的累加罢了,而前面这些通过当前移除的颜色个数i,j可以很容易的知道。所以,我们只需要记录每种颜色在两个序列中最开始出现的位置和最后出现的位置,那么便可以进行规划了。

刚开始自己一直在死磕,死活做不出,熬了一个星期左右,无奈看了紫书上面的思路,之后便动手开始自己实现,思路还是很清晰的,可以写出来之后样例一直过不去,然后就调试了一天半,后面才发现我写的程序的bug太多了,哎…

 

这道题思路巧妙而且细节甚多,有以下几点要考虑:

1、  怎么处理每个颜色的位置?设cnt[i][j]为分别取走i个颜色和j个颜色时还有多少种颜色已经出现但尚未结束,cnt[i][j]由cnt[i-1][j](i大于0时)和cnt[i][j-1](j大于0时)转移而来,只需要在转移的时候判断当前取的这个颜色是否处在开始位置或者最后位置就行了。

2、  一种颜色在一个序列中没有出现的时候该怎么处理它的开始位置和最后位置?很明显在预处理位置的时候会有影响,那么开始位置初始化为INF,最后位置初始化为0即可,当然可以if else,不过那样太过复杂。

这是最主要的两点,其他的也应该没什么了,转移方程很明显:

dp[i][j] = min(dp[i][j],dp[i-1][j]+cnt[i-1][j]); (i>0)

if (j) dp[i][j] = min(dp[i][j],dp[i][j-1]+cnt[i][j-1]) (j>0)

在我自己实现的过程中遇到了一个更无奈的问题…好不容易过了样例,以为终于可以松一口气了,结果TLE,瞬间人都傻了…一直在想怎么会T的,难道还要优化?实在优化不出来什么了,都是一些小的地方,优化了还是超时,又放了一天,几乎要被这题坑到吐了。。。后面请教别人才知道T的原因….居然是由于我的memset初始化操作导致的,怎么都不会想到。因为这题长度有5000,定义了两个5000*5000的数组,每个样例都memset了几百兆的内存,难怪一直TLE,以前一直以为memset是个很方便的快速清空函数,因为还可以对高维数组直接操作。现在才知道任何操作都是要付出代价的….

使用滚动数组或者不清空直接覆盖都可以,不过前者明显节省了很大比例的空间。

算是学到了很多的东西,写代码就是这样,细节绝对成败…

#include<cstdio>#include<cstring>#include<algorithm>#define INF 0x3f3f3f3f#define LL long long#define N 5005using namespace std;unsigned char c;int dp[N][N], cnt[N][N];int bf[100], bs[100], ef[100], es[100];char f[N], s[N];int main(){    int t;    scanf("%d", &t);    while (t--){        scanf("%s %s", f+1, s+1);        int lenf = strlen(f+1), lens = strlen(s+1);        for (c = 'A'; c <= 'Z'; c++) bf[c] = bs[c] = INF, ef[c] = es[c] = 0;        for (int i = 1; i <= lenf; i++){            c = f[i];            bf[c] = min(i, bf[c]);            ef[c] = i;        }        for (int i = 1; i <= lens; i++){            c = s[i];            bs[c] = min(i, bs[c]);            es[c] = i;        }        for (int i = 0; i <= lenf; i++){            for (int j = 0; j <= lens; j++){                if (i){                    cnt[i][j] = cnt[i-1][j];                    c = f[i];                    if (bf[c] == i && bs[c] > j) cnt[i][j]++;                    if (ef[c] == i && es[c] <= j) cnt[i][j]--;                }                if (j){                    cnt[i][j] = cnt[i][j-1];                    c = s[j];                    if (bs[c] == j && bf[c] > i) cnt[i][j]++;                    if (es[c] == j && ef[c] <= i) cnt[i][j]--;                }            }        }        for (int i = 0; i <= lenf; i++)            for (int j = 0; j <= lens; j++){                if (!i && !j) continue;                dp[i][j] = INF;                if (i) dp[i][j] = min(dp[i][j], dp[i-1][j]+cnt[i-1][j]);                if (j) dp[i][j] = min(dp[i][j], dp[i][j-1]+cnt[i][j-1]);            }        printf("%d\n", dp[lenf][lens]);    }    return 0;}

使用滚动数组时最好将cnt数组的处理与dp放在一块。

#include<cstdio>#include<cstring>#include<algorithm>#define INF 0x3f3f3f3f#define LL long long#define N 5005using namespace std;unsigned char c;int dp[2][N], cnt[2][N];int bf[100], bs[100], ef[100], es[100];char f[N], s[N];int main(){    int t;    scanf("%d", &t);    while (t--){        scanf("%s %s", f+1, s+1);        int lenf = strlen(f+1), lens = strlen(s+1);        for (c = 'A'; c <= 'Z'; c++) bf[c] = bs[c] = INF, ef[c] = es[c] = 0;        for (int i = 1; i <= lenf; i++){            c = f[i];            bf[c] = min(i, bf[c]);            ef[c] = i;        }        for (int i = 1; i <= lens; i++){            c = s[i];            bs[c] = min(i, bs[c]);            es[c] = i;        }        int t = 0;        for (int i = 0; i <= lenf; i++){            for (int j = 0; j <= lens; j++){                if (!i && !j) continue;                dp[t][j] = INF;                if (i) dp[t][j] = min(dp[t][j], dp[!t][j]+cnt[!t][j]);                if (j) dp[t][j] = min(dp[t][j], dp[t][j-1]+cnt[t][j-1]);                if (i){                    cnt[t][j] = cnt[!t][j];                    c = f[i];                    if (bf[c] == i && bs[c] > j) cnt[t][j]++;                    if (ef[c] == i && es[c] <= j) cnt[t][j]--;                }                if (j){                    cnt[t][j] = cnt[t][j-1];                    c = s[j];                    if (bs[c] == j && bf[c] > i) cnt[t][j]++;                    if (es[c] == j && ef[c] <= i) cnt[t][j]--;                }            }            t = !t;        }        printf("%d\n", dp[!t][lens]);        memset(cnt, 0, sizeof(int)*(lens+5));        memset(dp, 0, sizeof(int)*(lens+5));    }    return 0;}


0 0
原创粉丝点击