区间dp总结
刷了幾道區間水題
第一個
洛谷p4170
題目描述
假設你有一條長度為5的木版,初始時沒有涂過任何顏色。你希望把它的5個單位長度分別涂上紅、綠、藍、綠、紅色,用一個長度為5的字符串表示這個目標:RGBGR。
每次你可以把一段連續的木版涂成一個給定的顏色,后涂的顏色覆蓋先涂的顏色。例如第一次把木版涂成RRRRR,第二次涂成RGGGR,第三次涂成RGBGR,達到目標。
用盡量少的涂色次數達到目標。
輸入輸出格式
輸入格式:
輸入僅一行,包含一個長度為n的字符串,即涂色目標。字符串中的每個字符都是一個大寫字母,不同的字母代表不同顏色,相同的字母代表相同顏色。
輸出格式:
僅一行,包含一個數,即最少的涂色次數。
輸入輸出樣例
輸入樣例#1:
AAAAA
輸出樣例#1:
1
輸入樣例#2:
RGBGR
輸出樣例#2:
3
說明
40%的數據滿足:1<=n<=10
100%的數據滿足:1<=n<=50
一個很模版的題,也不需要處理環,用于區間入門是個很好的題
#include<bits/stdc++.h>
using namespace std;
int n=0,dp[51][51];
char a[55];
int main(){
scanf("%s",&a);
memset(dp,0x3f3f3f3f,sizeof(dp));
while(a[n]>='A'&&a[n]<='Z') n++;n--;
for(int i=0;i<=n;i++) dp[i][i]=1;
for(int l=1;l<=n;l++){
for(int i=0,j=l;j<=n;i++,j++){
if(a[i]==a[j]) dp[i][j]=min(dp[i+1][j],dp[i][j-1]);
else {
for(int k=i;k<j;k++){
int aa=dp[i][k]+dp[k+1][j];
dp[i][j]=min(dp[i][j],aa);
}
}
}
}
printf("%d",dp[0][n]);
return 0;
}
第二個 洛谷p1063
題目描述
在MarsMarsMars星球上,每個MarsMarsMars人都隨身佩帶著一串能量項鏈。在項鏈上有NNN顆能量珠。能量珠是一顆有頭標記與尾標記的珠子,這些標記對應著某個正整數。并且,對于相鄰的兩顆珠子,前一顆珠子的尾標記一定等于后一顆珠子的頭標記。因為只有這樣,通過吸盤(吸盤是MarsMarsMars人吸收能量的一種器官)的作用,這兩顆珠子才能聚合成一顆珠子,同時釋放出可以被吸盤吸收的能量。如果前一顆能量珠的頭標記為mmm,尾標記為rrr,后一顆能量珠的頭標記為r,尾標記為nnn,則聚合后釋放的能量為m×r×nm imes r imes nm×r×n(MarsMarsMars單位),新產生的珠子的頭標記為mmm,尾標記為nnn。
需要時,MarsMarsMars人就用吸盤夾住相鄰的兩顆珠子,通過聚合得到能量,直到項鏈上只剩下一顆珠子為止。顯然,不同的聚合順序得到的總能量是不同的,請你設計一個聚合順序,使一串項鏈釋放出的總能量最大。
例如:設N=4N=4N=4,444顆珠子的頭標記與尾標記依次為(2,3)(3,5)(5,10)(10,2)(2,3) (3,5) (5,10) (10,2)(2,3)(3,5)(5,10)(10,2)。我們用記號⊕表示兩顆珠子的聚合操作,(jjj⊕kkk)表示第j,kj,kj,k兩顆珠子聚合后所釋放的能量。則第444、111兩顆珠子聚合后釋放的能量為:
(444⊕111)=10×2×3=60=10 imes 2 imes 3=60=10×2×3=60。
這一串項鏈可以得到最優值的一個聚合順序所釋放的總能量為:
((444⊕111)⊕222)⊕333)=10×2×3+10×3×5+10×5×10=71010 imes 2 imes 3+10 imes 3 imes 5+10 imes 5 imes 10=71010×2×3+10×3×5+10×5×10=710。
輸入輸出格式
輸入格式:
第一行是一個正整數N(4≤N≤100)N(4≤N≤100)N(4≤N≤100),表示項鏈上珠子的個數。第二行是NNN個用空格隔開的正整數,所有的數均不超過100010001000。第iii個數為第iii顆珠子的頭標記(1≤i≤N)(1≤i≤N)(1≤i≤N),當i<Ni<Ni<N時,第iii顆珠子的尾標記應該等于第i+1i+1i+1顆珠子的頭標記。第NNN顆珠子的尾標記應該等于第111顆珠子的頭標記。
至于珠子的順序,你可以這樣確定:將項鏈放到桌面上,不要出現交叉,隨意指定第一顆珠子,然后按順時針方向確定其他珠子的順序。
輸出格式:
一個正整數E(E≤2.1×(10)9)E(E≤2.1 imes (10)^9)E(E≤2.1×(10)9),為一個最優聚合順序所釋放的總能量。
輸入輸出樣例
輸入樣例#1:
復制
4 2 3 5 10
輸出樣例#1: 復制
710
說明
NOIP 2006 提高組 第一題
嚴格來說這個題是我第一次接觸區間時做的,當時還沒有看懂,雖然現在也是半懂不懂
首先要處理環,把數組再擴大一倍就好
再思考轉移方程 用dp[i][j]表示從i到j的最優解(基本操作) dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+cost[i]*cost[k+1]*cost[j+1]) (其實就是枚舉斷點,再合并刷新max值)(還有一個細節,為什么是cost[i]*cost[k+1]*cost[j+1]?這個要自己手推一下了,其實不難理解)
#include<bits/stdc++.h>
using namespace std;
int n,e[210],dp[210][210];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",e+i),e[i+n]=e[i];
for(int j=2;j<2*n;j++){//注意是倒著枚舉,先枚舉j,不過順著枚舉不知會不會炸
for(int i=j-1;i>0;i--){
for(int k=i;k<j;k++){
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+e[i]*e[k+1]*e[j+1]);
}
}
}
int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,dp[i][i+n-1]);
printf("%d",ans);
return 0;
}
第三個,洛谷p3205合唱隊
題目描述
為了在即將到來的晚會上有更好的演出效果,作為AAA合唱隊負責人的小A需要將合唱隊的人根據他們的身高排出一個隊形。假定合唱隊一共N個人,第i個人的身高為Hi米(1000<=Hi<=2000),并已知任何兩個人的身高都不同。假定最終排出的隊形是A 個人站成一排,為了簡化問題,小A想出了如下排隊的方式:他讓所有的人先按任意順序站成一個初始隊形,然后從左到右按以下原則依次將每個人插入最終棑排出的隊形中:
-第一個人直接插入空的當前隊形中。
-對從第二個人開始的每個人,如果他比前面那個人高(H較大),那么將他插入當前隊形的最右邊。如果他比前面那個人矮(H較小),那么將他插入當前隊形的最左邊。
當N個人全部插入當前隊形后便獲得最終排出的隊形。
例如,有6個人站成一個初始隊形,身高依次為1850、1900、1700、1650、1800和1750,
那么小A會按以下步驟獲得最終排出的隊形:
1850
1850 , 1900 因為 1900 > 1850
1700, 1850, 1900 因為 1700 < 1900
1650 . 1700, 1850, 1900 因為 1650 < 1700
1650 , 1700, 1850, 1900, 1800 因為 1800 > 1650
1750, 1650, 1700,1850, 1900, 1800 因為 1750 < 1800
因此,最終排出的隊形是 1750,1650,1700,1850, 1900,1800
小A心中有一個理想隊形,他想知道多少種初始隊形可以獲得理想的隊形
輸入輸出格式
輸入格式:
輸出格式:
注意要mod19650827
輸入輸出樣例
輸入樣例#1:
復制
4 1701 1702 1703 1704
輸出樣例#1: 復制
8
說明
30%的數據:n<=100
100%的數據:n<=1000
這個題目就要仔細推一下狀態方程了,因為身高不同的人進入隊列的位置不同,所以要判斷,可以開兩個數組,f[i][j]表示最后一個人進來時是從前面進來,即i,g[i][j]則表示最后一個人從后面進來,即j,那么可以得到轉移方程:f[i][j]=f[i+1][j]*(a[i]<a[i+1])+g[i+1][j]*(a[j]>a[i]) g[i][j]=g[i][j-1]*(a[j]>a[j-1])+f[i][j-1]*(a[j]>a[i])
當然可以直接開一個三維數組,道理一樣
還有一點,在初始化時只需把f或g中一個處理了就好,因為處理兩個的話會重復計算
#include<bits/stdc++.h>
#define maxn 1010
#define mod 19650827
using namespace std;
int n,a[maxn],f[maxn][maxn],g[maxn][maxn];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",a+i);
for(int i=1;i<=n;i++) f[i][i]=1;
for(int l=1;l<n;l++){
for(int i=1,j=l+1;j<=n;j++,i++){
f[i][j]=f[i+1][j]*(a[i]<a[i+1])+g[i+1][j]*(a[i]<a[j]);f[i][j]%=mod;
g[i][j]=g[i][j-1]*(a[j]>a[j-1])+f[i][j-1]*(a[j]>a[i]);g[i][j]%=mod;
}
}
printf("%d",(g[1][n]+f[1][n])%mod);
return 0;
}//代碼是真的短。。
先做了這么多,等以后深入時再補充
未完待續
總結
- 上一篇: 暴雪战网安全令的原理
- 下一篇: 在线文本差异对比