Color Length UVA
阿新 • • 發佈:2018-12-10
題意:
輸入兩個長度分別為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])。
#include<iostream> #include<algorithm> #include<cstring> #include<string> using namespace std; const int maxn = 5000 + 5; const int INF = 10000000; char p[maxn], q[maxn]; int sp[26], sq[26], ep[26], eq[26]; int d[maxn][maxn], c[maxn][maxn]; int main() { //freopen("D:\\txt.txt", "r", stdin); int T, n, m; scanf("%d", &T); while (T--) { scanf("%s%s", p + 1, q + 1); //cout << p + 1 << " " << q + 1 << endl; n = strlen(p + 1); m = strlen(q + 1); //將字母轉化成數字 for (int i = 1; i <= n; i++) p[i] -= 'A'; for (int i = 1; i <= m; i++) q[i] -= 'A'; //預處理 for (int i = 0; i < 26; i++) { sp[i] = sq[i] = INF; ep[i] = eq[i] = 0; } //預處理,計算出序列1中每個字元的開始位置和結束位置 for (int i = 1; i <= n; i++) { sp[p[i]] = min(sp[p[i]], i); ep[p[i]] = i; } //預處理序列2 for (int i = 1; i <= m; i++) { sq[q[i]] = min(sq[q[i]], i); eq[q[i]] = i; } for (int i = 0; i <= n; i++) { for (int j = 0; j <= m; j++) { if (!i && !j) continue; int v1 = INF, v2 = INF; if (i) v1 = d[i-1][j] + c[i-1][j]; //從p中取顏色 if (j) v2 = d[i][j - 1] + c[i][j - 1]; //從q中取顏色 d[i][j] = min(v1, v2); //更新c陣列 if (i) { c[i][j] = c[i - 1][j]; if (sp[p[i]] == i && sq[p[i]] > j) c[i][j]++; if (ep[p[i]] == i && eq[p[i]] <= j) c[i][j]--; } else if (j) { c[i][j] = c[i][j - 1]; if (sq[q[j]] == j && sp[q[j]] > i) c[i][j]++; if (eq[q[j]] == j && ep[q[j]] <= i) c[i][j]--; } } } cout << d[n][m] << endl; } return 0; }