UVA-1625-Color Length(DP LCS變形)
阿新 • • 發佈:2018-10-07
https %s 出現 麻煩 開頭 表示 整體 continue 就是
Color Length(UVA-1625)(DP LCS變形)
題目大意
輸入兩個長度分別為n,m(<5000)的顏色序列。要求按順序合成同一個序列,即每次可以把一個序列開頭的顏色放到新序列的尾部。
https://odzkskevi.qnssl.com/a68cbd3e27f46b4f02ea12b7b1a1abca
然後產生的新序列中,對於每一個顏色c,都有出現的位置,L(c)表示最小位置和最大位置之差,求L(c)總和最小的新序列。
分析
- LCS 是公共上升子序列,在動態轉移的過程中,考慮第一個序列的 i ,和第二個序列的 j ,如果 i 和 j 相同,則
dp[i][j]=d[i-1][j-1]+1
dp[i][j]= max(dp[i-1][j],dp[i][j-1])
。完成動態轉移。 - 而這個題,是要讓L(c)最小,在動態轉移的過程中,我們要記錄的值就是L(c)的和,同時還要考慮是不是有顏色開始或者結束。為了轉移L(c)的和,我們還要用一個數組 C 來記錄當前有多少顏色開始但尚未結束。(思路紫薯說的非常清楚)
dp[i][j] = min(dp[i-1][j]+c[i-1][j],dp[i][j-1]+c[i][j-1])
i,j 表示前 i 個和前 j 個組成新串。- 做法就是先預處理兩個串中每個字符的首次出現位置和末端出現位置。然後DP。DP狀態轉移思路其實不難想到,但是需要有些新的細節考慮。
- 總結一些細節
- 因為在兩個串的合成過程中,可以有一個串一直未參與,而另一個串在一直合成。所以DP數組中下標0就是這個含義,但是如果字符串也從下標0開始,是不是有點麻煩了?所以第一個細節就是字符串從下標1開始。
- 可以先初始化 i = 0時候的情況,但是為了整體性,放在循環中處理會更好。(預處理的只能處理第一個為0,只取第二個串的情況,但是在DP過程中,每一層都要單獨處理只取第一個串,第二個串為0,所以不如直接放在循環中進行考慮)
const int maxn = 5000+5; const int INF = 1000000000; char p[maxn],q[maxn]; int sp[26],sq[26],ep[26],eq[26]; int d[maxn],c[maxn];//其他人博客貌似都是兩層,但其實一層就夠了。 int main() { int T;cin>>T; while(T--) { scanf("%s%s",p+1,q+1); int n = strlen(p+1),m = strlen(q+1); for(int i=1;i<=n;i++) p[i]-='A'; for(int j=1;j<=m;j++) q[j]-='A'; for(int i=0;i<26;i++) { sp[i] = sq[i] = INF; ep[i] = eq[i] = 0; } //get到這個預處理,以前還沒遇到過。 for(int i=1;i<=n;i++) { sp[p[i]] = min(sp[p[i]],i); ep[p[i]] = i; } for(int i=1;i<=m;i++) { sq[q[i]] = min(sq[q[i]],i); eq[q[i]] = i; } memset(c,0,sizeof c);memset(d,0,sizeof d); for(int i=0;i<=n;i++) { for(int j = 0;j<=m;j++) { if(!j&&!i)continue;//兩個都是0,就直接繼續。 int v1=INF,v2 = INF;//對於這種特殊情況,只能先把兩個值先寄存在v1,v2,並且賦值為INF。 if(i) v1=d[j]+c[j]; if(j) v2=d[j-1]+c[j-1]; d[j] = min(v1,v2); if(i) { c[j] = c[j]; if(sp[p[i]]==i&&sq[p[i]]>j)c[j]++;//if中的條件,這裏的細節也需要思考一下 if(ep[p[i]]==i&&eq[p[i]]<=j)c[j]--; } else if(j) { c[j] = c[j-1]; if(sq[q[j]] == j && sp[q[j]]>i)c[j]++; if(eq[q[j]] == j && ep[q[j]]<=i)c[j]--; } } } printf("%d\n",d[m]); } return 0; }
UVA-1625-Color Length(DP LCS變形)