日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

区间dp总结

發布時間:2024/1/4 33 生活家
生活随笔 收集整理的這篇文章主要介紹了 区间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;
}//代碼是真的短。。

先做了這么多,等以后深入時再補充

未完待續

總結

以上是生活随笔為你收集整理的区间dp总结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。