藍橋訓練之動態規划


入門

數組分組

題意:

一個數組可被分為n組,整個數組的權值被定義為各組內數字之積對1000取模后的總和,求數組的最大權值

思路:

我們用dp[i]表示前i個數分組的最大權值,對於位置i,我們可以枚舉最后一個分組的起始位置為j,計算i,j之間的權值,然后更新dp[i]即可。

為了避免過多的計算,我們需要預處理出來每個區間的乘積對1000取模的結果。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e3+100, mod = 1000;
int n, a[maxn], dp[maxn], pre[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++){
        pre[i][i] = a[i];
        for(int j = i+1; j <= n; j++)
            pre[i][j] = (pre[i][j-1]*a[j])%mod;
    }
    dp[1] = a[1];
    for(int i = 2; i <= n; i++){
        for(int j = 0; j < i ; j++)
            dp[i] = max(dp[i], dp[j]+pre[j+1][i]);
    }
    printf("%d", dp[n]);
}
View Code

 

牆壁塗色

題意:

一個環形的房間被分成n塊,每塊的顏色可以為R、G、B,但是相鄰兩快的顏色必須不同,求塗色的方案數

思路:

這是道遞推的題目,用dp[i]表示長度為i的方案數

遞推的本質:用已知推未知,尋找前后項之間的關系。仔細觀察發現可以分為兩種情況,當第1項和第n-1項不同色時,第n項只能取一種顏色;當兩者取相同顏色時,第n項可以取兩種顏色,於是得到遞推表達式:dp[n] = dp[n-1] + 2*dp[n-2]

最后注意初始化的時候需要初始化dp[0]、dp[1]、dp[2](原因稍加思考便知),並且輸出使用long long否則溢出

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn = 1e5+100;
ll n, a[maxn], dp[maxn];
int main(){
    scanf("%lld", &n);
    dp[1] = 3, dp[2] = 6, dp[3] = 6;
    for(int i = 4; i <= n; i++)
        dp[i] = dp[i-1] + 2*dp[i-2];
    printf("%lld", dp[n]);
}
View Code

 

過河 [POJ 1700. Crossing River]

題意:

有n個人准備過河,他們只有一只船,這只船每次只能通過坐不超過2人,每個人都有不同的划船速度;同一組過河的人的速度是由較慢的速度來確定。你的任務是確定一個策略,最大限度的減少他們過河的時間(使他們最快的過河)

思路:

我們先看下樣例,四個人過橋花費的時間分別為 1 2 5 10,具體步驟是這樣的:

第一步:1和2過去,花費時間2,然后1回來(花費時間1)
第二歩:3和4過去,花費時間10,然后2回來(花費時間2)
第三歩:1和2過去,花費時間2,總耗時17

這道題目更像是道貪心的題目,拿到題目后可以用數學歸納法求最短花費時間,可以發現可以有兩種貪心策略:

1.考慮划船快的人划的次數最多 [最快的人依次把最慢的兩個人渡過河再回來]

2.考慮划的慢的人盡量少占用划的快的人的時間. [最快的兩個人先過河,最快的人回來,然后最慢的兩個過河,第二快的回來。直到剩余人數少於4人為止]

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e3+100;
int a[maxn];
int cal(int n) {
    int ans = 0;
    while (n >= 4) {
        // 最快的人把最慢的兩個人渡過河消耗的時間
        int s1 = a[n] + a[n - 1] + a[1] * 2;
        // 最快的兩個人把最慢的兩個人渡過河消耗的時間
        int s2 = a[2] * 2 + a[1] + a[n];
        ans += min(s1, s2);
        // 一次渡河之后不管選哪種渡河方式,最快的兩個人都要回來
        n -= 2;
    }
    if (n==2) return ans + a[2];
    for(int i = 1; i <= n; i++) ans += a[i];
    return ans;
}
int main() {
    int t, n;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
        sort(a+1, a+1+n);
        printf("%d\n", cal(n));
    }
    return 0;
}
View Code

 Reference:

https://www.cnblogs.com/xiaoyezi-wink/p/11183637.html

https://blog.nowcoder.net/n/2467bddb99424462b1ce2f7f26d5f31a

https://www.git2get.com/av/38084571.html

 

最大子段和

蒜頭君的最大子段和

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define inf 1e12
#define ll long long
using namespace std;
const int maxn = 1e6+100;
ll n, a[maxn], dp[maxn], ans = -inf;
int main(){
    scanf("%lld", &n);
    for(int i = 1; i <= n; i++){
        scanf("%lld", &a[i]);
        dp[i] = max(a[i], dp[i-1]+a[i]);
        ans = max(ans, dp[i]);
    }
    printf("%lld", ans);
} 
View Code

 

蒜頭君的最大子矩陣和

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn = 1e3+100;
ll n, m, tmp;
ll sum[maxn][maxn], dp[maxn], ans = -inf;
int main(){
    scanf("%lld %lld", &n, &m);
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            scanf("%lld", &tmp);
            sum[i][j] = sum[i-1][j]+tmp;
        }
    }
    for(int i = 1; i <= n; i++){
        for(int k = 0; k < i; k++){
            for(int j = 1; j <= m; j++){
                dp[j] = max(dp[j-1], 0ll) + sum[i][j]-sum[k][j];
                ans = max(ans, dp[j]);
            }
        }
    }
    printf("%lld", ans);
} 
View Code

 

最長公共子序列

最長公共子序列

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn = 1e3+100;
char a[maxn], b[maxn];
int dp[maxn][maxn];
int main(){
    scanf("%s%s", a+1, b+1);
    int n = strlen(a+1), m = strlen(b+1);
    for(int i =  1; i <= n ;i++){
        for(int j = 1; j <= m; j++){
            dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
            if(a[i]==b[j]) dp[i][j] = max(dp[i][j], dp[i-1][j-1]+1);
        }
    }
    printf("%d", dp[n][m]);
}
View Code

 

編輯距離

回文串

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn = 3e3+100;
char s[maxn];
int dp[maxn][maxn];
int main(){
    scanf("%s", s+1);
    int n = strlen(s+1);
    for(int i = n-1; i >= 1; i--){
        for(int j = i+1; j <= n; j++){
            if(s[i]==s[j]) dp[i][j] = dp[i+1][j-1];
            else dp[i][j] = min(dp[i+1][j], dp[i][j-1]) + 1;
        }
    }    
    printf("%d", dp[1][n]);
}
Origin
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn = 1e4+100;
char s[maxn];
int dp[maxn];
int main(){
    scanf("%s", s+1);
    int n = strlen(s+1);
    for(int i = n-1; i >= 1; i--){
        int pre = 0;
        for(int j = i+1; j <= n; j++){
            int tmp = dp[j];
            if(s[i]==s[j]) dp[j] = pre;
            else dp[j] = min(dp[j], dp[j-1]) + 1;
            pre = tmp;
        }
    }    
    printf("%d", dp[n]);
}
狀態壓縮

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM