https://vjudge.net/problem/UVA-1625
题意:
输入两个长度分别为n和m的颜色序列,要求按顺序合并成同一个序列,即每次可以把一个序列开头的颜色放到新序列的尾部。对于每个颜色c来说,其跨度L(c)等于最大位置和最小位置之差。
思路:
我们用d(i,j)表示两个序列已经分别移走了i和j个元素时的最小代价。当然为了在状态转移时知道每个字母的状态,我们需要一些预处理。在下面的代码中,sp,ep数组分别用来表示序列1中每个字母的开头位置和结束位置,同样的,sq,eq分别用来表示序列1中每个字母的开头位置和结束位置。每次新增一个字符后,所有已经出现的但没有结束的字符的跨度L(c)都要+1。所以,我们还需要设置一个c数组来记录已经开始但还没有结束的字符数。
这样,转移方程就是dp(i,j)=min(dp(i-1,j)+c[i-1][j],dp(i,j-1)+c[i][j-1])。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<string> 5 using namespace std; 6 7 const int maxn = 5000 + 5; 8 const int INF = 10000000; 9 10 char p[maxn], q[maxn]; 11 int sp[26], sq[26], ep[26], eq[26]; 12 int d[maxn][maxn], c[maxn][maxn]; 13 14 int main() 15 { 16 //freopen("D:\txt.txt", "r", stdin); 17 int T, n, m; 18 scanf("%d", &T); 19 while (T--) 20 { 21 scanf("%s%s", p + 1, q + 1); 22 //cout << p + 1 << " " << q + 1 << endl; 23 n = strlen(p + 1); 24 m = strlen(q + 1); 25 26 //将字母转化成数字 27 for (int i = 1; i <= n; i++) p[i] -= 'A'; 28 for (int i = 1; i <= m; i++) q[i] -= 'A'; 29 30 //预处理 31 for (int i = 0; i < 26; i++) 32 { 33 sp[i] = sq[i] = INF; 34 ep[i] = eq[i] = 0; 35 } 36 37 //预处理,计算出序列1中每个字符的开始位置和结束位置 38 for (int i = 1; i <= n; i++) 39 { 40 sp[p[i]] = min(sp[p[i]], i); 41 ep[p[i]] = i; 42 } 43 44 //预处理序列2 45 for (int i = 1; i <= m; i++) 46 { 47 sq[q[i]] = min(sq[q[i]], i); 48 eq[q[i]] = i; 49 } 50 51 for (int i = 0; i <= n; i++) 52 { 53 for (int j = 0; j <= m; j++) 54 { 55 if (!i && !j) continue; 56 int v1 = INF, v2 = INF; 57 if (i) v1 = d[i-1][j] + c[i-1][j]; //从p中取颜色 58 if (j) v2 = d[i][j - 1] + c[i][j - 1]; //从q中取颜色 59 d[i][j] = min(v1, v2); 60 61 //更新c数组 62 if (i) 63 { 64 c[i][j] = c[i - 1][j]; 65 if (sp[p[i]] == i && sq[p[i]] > j) c[i][j]++; 66 if (ep[p[i]] == i && eq[p[i]] <= j) c[i][j]--; 67 } 68 69 else if (j) 70 { 71 c[i][j] = c[i][j - 1]; 72 if (sq[q[j]] == j && sp[q[j]] > i) c[i][j]++; 73 if (eq[q[j]] == j && ep[q[j]] <= i) c[i][j]--; 74 } 75 } 76 } 77 cout << d[n][m] << endl; 78 } 79 return 0; 80 }
接下来再附上一个滚动数组的代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 7 const int maxn = 5000 + 5; 8 const int INF = 1000000000; 9 10 char p[maxn], q[maxn]; 11 int sp[26], sq[26], ep[26], eq[26]; 12 int d[2][maxn], c[2][maxn]; 13 14 int main() 15 { 16 int T; 17 scanf("%d", &T); 18 while(T--) 19 { 20 scanf("%s%s", p+1, q+1); 21 22 int n = strlen(p+1); 23 int m = strlen(q+1); 24 for(int i = 1; i <= n; i++) p[i] -= 'A'; 25 for(int i = 1; i <= m; i++) q[i] -= 'A'; 26 27 // calculate s and e 28 for(int i = 0; i < 26; i++) 29 { 30 sp[i] = sq[i] = INF; 31 ep[i] = eq[i] = 0; 32 } 33 for(int i = 1; i <= n; i++) 34 { 35 sp[p[i]] = min(sp[p[i]], i); 36 ep[p[i]] = i; 37 } 38 for(int i = 1; i <= m; i++) 39 { 40 sq[q[i]] = min(sq[q[i]], i); 41 eq[q[i]] = i; 42 } 43 44 // dp 45 int t = 0; 46 memset(c, 0, sizeof(c)); 47 memset(d, 0, sizeof(d)); 48 for(int i = 0; i <= n; i++) 49 { 50 for(int j = 0; j <= m; j++) 51 { 52 if(!i && !j) continue; 53 54 // calculate d 55 int v1 = INF, v2 = INF; 56 //计算d[i][j], d[i][j]由d[i-1][j]或d[i][j-1]添加一个字母得到 57 if(i) v1 = d[t^1][j] + c[t^1][j]; // remove from p 58 if(j) v2 = d[t][j - 1] + c[t][j - 1]; // remove from q 59 d[t][j] = min(v1, v2); 60 61 // calculate c 62 if(i) 63 { 64 c[t][j] = c[t^1][j]; 65 if(sp[p[i]] == i && sq[p[i]] > j) c[t][j]++; //出现新的字母 66 if(ep[p[i]] == i && eq[p[i]] <= j) c[t][j]--; //一个字母已经结束 67 } 68 else if(j) 69 { 70 c[t][j] = c[t][j - 1]; 71 if(sq[q[j]] == j && sp[q[j]] > i) c[t][j]++; 72 if(eq[q[j]] == j && ep[q[j]] <= i) c[t][j]--; 73 } 74 } 75 t ^= 1; 76 } 77 printf("%d ", d[t^1][m]); 78 } 79 return 0; 80 }