Uva 1625 - Color Length(DP)
題目鏈接 https://cn.vjudge.net/problem/UVA-1625
【題意】
輸入兩個長度分別為n和m的顏色序列(n,m<=5000),要求按一定規則合并成一個序列,規則是每次可以把一個序列開頭的顏色放到新序列的尾部。例如對于序列GBBY和YRRGB,它們可以合成很多中結果,其中包含這樣兩種結果,GBYBRYRGB和YRRGGBBYB,對于每個顏色c來說,其跨度L(c)等于新序列中顏色c出現的最大位置和最小位置之差,比如對于上面的兩種結果,每個顏色的L(c)和相應的總和如下表所示
| GBYBRYRGB | 7 | 3 | 7 | 2 | 19 |
| YRRGGBBYB | 1 | 7 | 3 | 1 | 12 |
你的任務是找到一種最合理的合并方式,使得新序列的L(c)總和最小
【思路】
???????紫書276頁的一道例題,一開始連狀態轉移怎么轉都想不出來,看了紫書的講解也也很暈,最后是看了好多別人的題解之后才弄明白的。首先dp的狀態就是書上所講,但我的求解順序和書上是反過來的,dp(i,j)表示的是從第一個序列頭部取走i個元素,第二個序列頭部取走j個元素的狀態下當前的花費值,所以最后的答案就是dp(n,m). 不難想到dp(i,j)的狀態一定是從dp(i-1,j)和dp(i,j-1)的狀態轉移而來的,所以狀態轉移方程就一定是這樣一個類似于LCS問題的式子
???????dp(i,j) = min {dp(i-1,j), dp(i,j-1)} + x
???????那這個x是個什么,按照書上所說,每次狀態轉移的時候,都要把所有的“已經出現但還沒結束”的顏色的L(c)值加1,所以在dp(i-1,j)向dp(i,j)的轉移過程中,這個x就應該是第一個字符串的前i-1個字符和第二個字符串的前j個字符中所有“已經出現但還沒結束”的字符個數,同理在dp(i,j-1)向dp(i,j)的轉移過程中,這個x就應該是第一個字符串的前i個字符和第二個字符串的前j-1個字符中所有“已經出現但還沒結束”的字符個數
???????結合下面的表格,比如兩個原始序列為題目描述中的GBBY和YRRGB,現在的狀態是dp(1,3)也就是從第一個字符串中取出G,第二個字符串中取出YRR,假設新序列是YRRG,現在要向著dp(1,4)做狀態轉移,也就是要再從第二個字符串中把G取出來,這時字母Y的頭上要加1,G的頭上要加1,R不用管因為R已經全部結束了,兩個字符串中已經沒有R了。所以現在的狀態轉移x的值為2,也就是dp(1,3)對應的新串中“已經出現但還沒結束”的字符個數。
| dp(1,3) | YRRG | BBY | GB |
| dp(1,4) | YRRGG | BBY | B |
???????那這個x的值到底怎么求呢,這就需要依賴于dp前的預處理了,用兩個數組f[2][26]和g[2][26]分別記錄每個字母在每個字符串中第一次出現的位置,最后一次出現的位置,有了這樣兩個數組,在dp過程中,另開一個c數組,c[i][j]記錄當前(i,j)狀態下的新串中“已經出現但還沒結束”的字符個數,那么最終的狀態轉移方程就是dp(i,j) = min {dp(i-1,j)+c[i-1][j],dp(i,j-1)+c[i][j-1]} 在dp過程進行的時候借助于f和g不斷更新c數組即可,注意數組c的結果也是遞推計算得到的。
#include<bits/stdc++.h> using namespace std;const int inf=2e9; const int maxn=5050;char s[2][maxn]; int len[2]; int f[2][26],g[2][26];//記錄每個字母在每個字符串中的第一次和最后一次出現位置 int dp[maxn][maxn],c[maxn][maxn];//c[i][j]記錄當前狀態下新串中出現過但還沒結束的字符的個數 void init(){//預處理 //初始化 for(int k=0;k<2;++k){for(int ch=0;ch<26;++ch){f[k][ch]=inf;g[k][ch]=-1;}}for(int k=0;k<2;++k){//處理每個字符串 for(int ch=0;ch<26;++ch){//處理每個字母 for(int i=1;i<=len[k];++i){//記錄第一次出現的位置 if(ch+'A'==s[k][i]){f[k][ch]=i;break;}}for(int i=len[k];i>=1;--i){//記錄最后一次出現的位置 if(ch+'A'==s[k][i]){g[k][ch]=i;break;}}}} }void solve(){dp[0][0]=0;c[0][0]=0;for(int i=0;i<=len[0];++i){for(int j=0;j<=len[1];++j){if(0==i && 0==j) continue;//計算當前狀態dp[i][j],一定由dp[i-1][j]和dp[i][j-1]轉移而來 dp[i][j]=inf;if(i>0) {//由dp[i-1][j]轉移而來,取出的是s[0][i] dp[i][j]=min(dp[i][j],dp[i-1][j]+c[i-1][j]);c[i][j]=c[i-1][j];int ch=s[0][i]-'A';if(i==f[0][ch] && j<f[1][ch]) ++c[i][j];//判斷s[0][i]是不是在新串中第一次出現 if(i==g[0][ch] && j>=g[1][ch]) --c[i][j];//判斷s[0][i]是不是在新串中最后一次出現 }if(j>0) {//由dp[i][j-1]轉移而來,取出的是s[1][j] dp[i][j]=min(dp[i][j],dp[i][j-1]+c[i][j-1]);c[i][j]=c[i][j-1];int ch=s[1][j]-'A';if(j==f[1][ch] && i<f[0][ch]) ++c[i][j];if(j==g[1][ch] && i>=g[0][ch]) --c[i][j];}}}printf("%d\n",dp[len[0]][len[1]]); }int main(){int t;scanf("%d",&t);while(t--){for(int k=0;k<2;++k) {scanf("%s",1+s[k]);//字符串的下標從1開始便于處理 len[k]=strlen(1+s[k]);}init();solve();}return 0; }拿滾動數組做優化,還可以進一步優化空間復雜度
#include<bits/stdc++.h> using namespace std;const int inf=2e9; const int maxn=5050;char s[2][maxn]; int len[2]; int f[2][26],g[2][26];//記錄每個字母在每個字符串中的第一次和最后一次出現位置 int dp[2][maxn],c[2][maxn]; void init(){//預處理 //初始化 for(int k=0;k<2;++k){for(int ch=0;ch<26;++ch){f[k][ch]=inf;g[k][ch]=-1;}}for(int k=0;k<2;++k){//處理每個字符串 for(int ch=0;ch<26;++ch){//處理每個字母 for(int i=1;i<=len[k];++i){//記錄第一次出現的位置 if(ch+'A'==s[k][i]){f[k][ch]=i;break;}}for(int i=len[k];i>=1;--i){//記錄最后一次出現的位置 if(ch+'A'==s[k][i]){g[k][ch]=i;break;}}}} }void solve(){dp[0][0]=0;c[0][0]=0;for(int i=0;i<=len[0];++i){for(int j=0;j<=len[1];++j){if(0==i && 0==j) continue;//計算當前狀態dp[i][j],存到滾動數組中的dp[i%2][j]的位置 int now=i%2, pre=1-now;dp[now][j]=inf;if(i>0) {dp[now][j]=min(dp[now][j],dp[pre][j]+c[pre][j]);c[now][j]=c[pre][j];int ch=s[0][i]-'A';if(i==f[0][ch] && j<f[1][ch]) ++c[now][j];if(i==g[0][ch] && j>=g[1][ch]) --c[now][j];}if(j>0) {dp[now][j]=min(dp[now][j],dp[now][j-1]+c[now][j-1]);c[now][j]=c[now][j-1];int ch=s[1][j]-'A';if(j==f[1][ch] && i<f[0][ch]) ++c[now][j];if(j==g[1][ch] && i>=g[0][ch]) --c[now][j];}}}printf("%d\n",dp[len[0]%2][len[1]]); }int main(){int t;scanf("%d",&t);while(t--){for(int k=0;k<2;++k) {scanf("%s",1+s[k]);//字符串的下標從1開始便于處理 len[k]=strlen(1+s[k]);}init();solve();}return 0; }轉載于:https://www.cnblogs.com/wafish/p/10465333.html
總結
以上是生活随笔為你收集整理的Uva 1625 - Color Length(DP)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DS3231时钟模块使用,IIC协议实践
- 下一篇: VBA 收集 Word关键字批量处理-E