区间dp 例题
D - 石子合并問題--直線版?
HRBUST - 1818?
?
這個題目是一個區間dp的入門,寫完這個題目對于區間dp有那么一點點的感覺,不過還是不太會。
注意這個區間dp的定義
dp[i][j] 表示的應該是將連續的從 i 到第 j 堆的石塊進行合并的最大值(或者最小值)
知道這個定義就很好求了,所以我們每次都要先枚舉這個區間的長度,然后就是枚舉這個區間的起點,然后就是枚舉分段點。
?
#include <cstdio> #include <cstring> #include <cstdlib> #include <queue> #include <cmath> #include <stack> #include <vector> #include <algorithm> #define pi acos(-1) #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn = 110; int dpmax[maxn][maxn], dpmin[maxn][maxn]; int stone[maxn]; int sum[maxn]; int main() {int n;while (scanf("%d", &n)!=EOF) {memset(dpmin, inf, sizeof(dpmin));memset(dpmax, -inf, sizeof(dpmax));memset(sum, 0, sizeof(sum));for (int i = 1; i <= n; i++) {scanf("%d", &stone[i]);sum[i] = sum[i - 1] + stone[i];dpmax[i][i] = 0;dpmin[i][i] = 0;}for (int i = 2; i <= n; i++) {for (int j = 1; j + i <= n + 1; j++) {int ends = j + i - 1;for (int k = j; k < ends; k++) {dpmin[j][ends] = min(dpmin[j][ends], dpmin[j][k] + dpmin[k + 1][ends] + sum[ends] - sum[j - 1]);dpmax[j][ends] = max(dpmax[j][ends], dpmax[j][k] + dpmax[k + 1][ends] + sum[ends] - sum[j - 1]);}}}printf("%d %d\n", dpmin[1][n], dpmax[1][n]);}return 0; } 區間dp入門?
?
D. XOR-pyramid
?
這個要是把題目看清楚了+看了一點點的題解就很簡單了。
這個題目需要對題目進行一部分處理,推薦學長博客?https://blog.csdn.net/qq_39599067/article/details/80335949
學長博客講的很清楚了。
我說一下我的理解吧,f[i][j]表示從i到j的異或和,dp[i][j]表示從i到j的最大的異或和
因為f[i][j]=f[i][j+1]^f[i+1][j]
所以這個dp就很好轉移了 dp[i][j]=max(f[i][j],max(dp[i][j-1],dp[i+1][j])
這個轉移方程我覺得還比較難理解,對于dp[i][j] 應該是由三個狀態轉移過來的,一個是本身的異或和,一個不包括左端點的最大值,一個是不包括右端點的最大值,
這個可以去看學長的那個圖。
?
#include <cstdio> #include <iostream> #include <cstdlib> #include <cstring> #include <queue> #include <vector> #include <algorithm> #define inf 0x3f3f3f3f using namespace std; const int maxn = 5e3 + 100; int dp[maxn][maxn], f[maxn][maxn];int main() {int n;scanf("%d", &n);for(int i=1;i<=n;i++){scanf("%d", &dp[i][i]);f[i][i] = dp[i][i];}for(int i=1;i<=n;i++){for(int j=1;j+i<=n;j++){f[j][j + i] = f[j][j + i - 1] ^ f[j + 1][j + i];}}for(int i=1;i<=n;i++){for(int j=1;j+i<=n;j++){dp[j][j + i] = max(f[j][i + j], max(dp[j][j + i - 1], dp[j + 1][j + i]));}}int q;scanf("%d", &q);while(q--){int l, r;scanf("%d%d", &l, &r);printf("%d\n", dp[l][r]);}return 0; } 區間dp?
B - Halloween Costumes
這個題目我覺得還是一個比較難的區間dp,相對于這個之前可以說算是裸題了。
這個我看了題解之后,按照題解的思路去敲代碼,推薦博客:https://www.cnblogs.com/DOLFAMINGO/p/7927432.html?
他是把這個題目進行了一定的轉化,轉化成了涂色,就是把給定一個區間,每次可以為一段連續的子區間刷一種顏色。問最少需要刷多少次,能得到目標的區間。
該題解對左端點進行討論,這個算是一個切入點吧,因為對于左端點和右端點進行討論應該都是可以的,但是對于左端點會更加簡單一些。
怎么對左端點進行討論呢
我的理解:
預處理就是假設每一個都是要涂成不一樣的顏色
這個應該是理解為先涂成目標這樣子,然后去判斷涂了多少層。
對左端點進行考慮。
先假設左端點涂的是一種新顏色。
所以dp[i][j]=dp[i+1][j]+1;
然后去判斷這個區間有沒有和它一樣的顏色,如果有的話,他們應該是同一個時刻涂的,
所以就暫時忽略它,以這個點為分段點對該點左右顏色總和加起來 去最小。
其實看了題解理解我覺得還是挺難的,能想到的人,我感覺挺變態的。
?
#include <cstdio> #include <cstdlib> #include <queue> #include <vector> #include <algorithm> #include <cstring> #include <iostream> #define inf 0x3f3f3f3f using namespace std; const int maxn = 1e2 + 10; int dp[maxn][maxn]; int a[maxn]; int main() {int t;scanf("%d", &t);for(int cas=1;cas<=t;cas++){int n;scanf("%d", &n);for (int i = 1; i <= n; i++) scanf("%d", &a[i]);memset(dp, 0, sizeof(dp));for (int i = 1; i <= n; i++) dp[i][i] = 1;for(int i=2;i<=n;i++){for(int j=1;j+i-1<=n;j++){int ends = i + j - 1;dp[j][ends] = dp[j + 1][ends] + 1;for(int k=j+1;k<=ends;k++){if(a[j]==a[k]){dp[j][ends] = min(dp[j][ends], dp[j][k - 1] + dp[k + 1][ends]);}}}}printf("Case %d: %d\n",cas, dp[1][n]);}return 0; } 區間dp?
?
H - String painter
?
來寫這個題目,如果你解決了上面的這個題目,那么這個就變得很簡單了,
從上面得題目我們知道了怎么去把一個空白字符串涂成我們想要的字符串。
現在我們是把一個已知的字符串涂成我們想要的,這個怎么寫呢?
這個應該是去枚舉每一位的字符,如果這個字符和所求的字符相同就不需要再涂了,
但是如果不一樣就需要給它重新涂色,這個涂色可不是簡單的加上一個顏色,
而是要從整體的角度去看如何給她涂色最優,所以我們要枚舉這個和這個之前的每一個位置來判斷最優。
?區間dp
轉載于:https://www.cnblogs.com/EchoZQN/p/10935990.html
總結
- 上一篇: 爬虫之Selenium
- 下一篇: 亚洲和欧洲的气候,3条及世界年平均降水量