区间dp学习、

来源:互联网 发布:特朗普移民政策 知乎 编辑:程序博客网 时间:2024/06/05 21:16

参考学习:传送门

POJ 2955

题意:给出一个由'(', ')', '[', ']'组成的字符串,现在让你找出一个子序列出来使得子序列是合法括号匹配,求出子序列的最大括号匹配。

思路:dp[i][j]代表区间[i, j] 的最大匹配,转移的话看代码吧

#include <cstdio>#include <cstring>#include <map>#include <algorithm>#include <iostream>#include <vector>#include <stack>using namespace std;#define LL long long#define pb push_back#define mk make_pair#define pill pair<int, int>#define mst(a, b)memset(a, b, sizeof a)#define REP(i, x, n)for(int i = x; i <= n; ++i)const int MOD = 1e9 + 7;const int qq = 105;char st[qq];int dp[qq][qq];int main(){while(scanf("%s", st) != EOF) {mst(dp, 0);if(st[0] == 'e')break;int len = strlen(st);for(int i = 1; i < len; ++i){for(int j = 0, k = i; k < len; ++j, ++k){if((st[j] == '(' && st[k] == ')') || (st[j] == '[' && st[k] == ']')){dp[j][k] = dp[j + 1][k - 1] + 2;}for(int x = j; x < k; ++x) {dp[j][k] = max(dp[j][k], dp[j][x] + dp[x + 1][k]);}}}printf("%d\n", dp[0][len - 1]);}return 0;}



HDU2476

题意:给出两个串A和B,可以对A串进行操作,对A的某个区间填充一个字符,问最小操作次数使A变成B串

思路:参考了网上的博客区间dp,基本思路是求空串变成B串要求的dp[i][j],然后考虑A串

#include <cstdio>#include <cstring>#include <cmath>#include <iostream>#include <algorithm>#include <sstream>#include <map>#include <set>#include <vector>#include <utility>#include <queue>#include <stack>using namespace std;#define LL long long#define pb push_back#define mk make_pair#define pill pair<int, int>#define mst(a, b)memset(a, b, sizeof a)#define REP(i, x, n)for(int i = x; i <= n; ++i)const int MOD = 1e9 + 7;const int qq = 105;int dp[qq][qq];char str1[qq], str2[qq];int ans[qq];int main(){while(scanf("%s%s", str1, str2) == 2) {mst(dp, 0);int n = strlen(str1);for(int i = 0; i < n; ++i) {for(int j = i; j < n; ++j) {dp[i][j] = j - i + 1;}}for(int i = n - 2; i >= 0; --i) {for(int j = i + 1; j < n; ++j) {dp[i][j] = dp[i + 1][j] + 1;for(int k = i + 1; k <= j; ++k) {if(str2[i] == str2[k]) {dp[i][j] = min(dp[i][j], dp[i + 1][k - 1] + dp[k][j]);}}}}for(int i = 0; i < n; ++i) {ans[i] = dp[0][i];if(str1[i] == str2[i]) {if(i == 0)ans[i] = 0;elseans[i] = ans[i - 1];}for(int k = 0; k < i; ++k) {ans[i] = min(ans[i], ans[k] + dp[k + 1][i]);}}printf("%d\n", ans[n - 1]);}return 0;}


POJ1141

参考:传送门

这方法是真的巧妙,QAQ

#include <cstdio>#include <cstring>#include <cmath>#include <iostream>#include <algorithm>#include <sstream>#include <map>#include <set>#include <vector>#include <utility>#include <queue>#include <stack>using namespace std;#define LL long long#define pb push_back#define mk make_pair#define pill pair<int, int>#define mst(a, b)memset(a, b, sizeof a)#define REP(i, x, n)for(int i = x; i <= n; ++i)const int MOD = 1e9 + 7;const int qq = 100 + 10;int d[qq][qq], c[qq][qq] = {-1};int len;string st;void Dp() {int i, j, k, l;int minx;for(i = 0; i < len; ++i) d[i][i] = 1;for(l = 1; l < len; ++l) {for(i = 0; i + l < len; ++i) {j = i + l;minx = d[i][i] + d[i + 1][j];c[i][j] = i;for(k = i + 1; k < j; ++k) {if(d[i][k] + d[k + 1][j] < minx) {minx = d[i][k] + d[k + 1][j];c[i][j] = k;}}d[i][j] = minx;if((st[i] == '(' && st[j] == ')') || (st[i] == '[' && st[j] == ']')) {if(d[i + 1][j - 1] < minx) {d[i][j] = d[i + 1][j - 1];c[i][j] = -1;}}}}}void Print(int i, int j) {if(i > j)return;if(i == j) {if(st[i] == '(' || st[i] == ')')cout << "()";elsecout << "[]";}else {if(c[i][j] >= 0) {Print(i, c[i][j]);Print(c[i][j] + 1, j);}else {if(st[i] == '(') {cout << "(";Print(i + 1, j - 1);cout << ")";} else {cout << "[";Print(i + 1, j - 1);cout << "]";}}}}int main(){cin >> st;len = st.size();Dp();Print(0, len - 1);cout << endl;return 0;}


codeforces 149D

题意:给出一个合法的括号序列,现在有三个条件
1.每一个括号要么没有颜色,要么红色,要么蓝色

2.每一对匹配的括号中有且只有一个有颜色

3.相邻的两个括号颜色不能相同。

现在问有多少种合法的填色方法


果然还是太弱了,完全没想到dfs- - 、

参考了某位聚聚的博客才理解这种做法。

dp[l][r][i][j] 代表区间[l, r] l涂i颜色,r涂j颜色的合法方式的种数

然后匹配区间进行递归,最后由小区间更新大区间。

#include <cstdio>#include <cstring>#include <cmath>#include <iostream>#include <algorithm>#include <sstream>#include <map>#include <set>#include <vector>#include <utility>#include <queue>#include <stack>using namespace std;#define LL long long#define pb push_back#define mk make_pair#define pill pair<int, int>#define mst(a, b)memset(a, b, sizeof a)#define REP(i, x, n)for(int i = x; i <= n; ++i)const int MOD = 1e9 + 7;const int qq = 705;LL dp[qq][qq][3][3];int match[qq];int Stack[qq];char st[qq];void Dfs(int l, int r) {if(l + 1 == r) {dp[l][r][0][1] = dp[l][r][0][2] = dp[l][r][1][0] = dp[l][r][2][0] = 1;return;}if(match[l] == r) {Dfs(l + 1, r - 1);for(int i = 0; i < 3; ++i) {for(int j = 0; j < 3; ++j) {if(j != 1)dp[l][r][0][1] = (dp[l][r][0][1] + dp[l + 1][r - 1][i][j]) % MOD;if(j != 2)dp[l][r][0][2] = (dp[l][r][0][2] + dp[l + 1][r - 1][i][j]) % MOD;if(i != 1)dp[l][r][1][0] = (dp[l][r][1][0] + dp[l + 1][r - 1][i][j]) % MOD;if(i != 2)dp[l][r][2][0] = (dp[l][r][2][0] + dp[l + 1][r - 1][i][j]) % MOD;}}} else {Dfs(l, match[l]);Dfs(match[l] + 1, r);for(int i = 0; i < 3; ++i) {for(int j = 0; j < 3; ++j) {for(int k = 0; k < 3; ++k) {for(int h = 0; h < 3; ++h) {if(j && j == k)continue;dp[l][r][i][h] = (dp[l][r][i][h] + (dp[l][match[l]][i][j] * dp[match[l] + 1][r][k][h] % MOD + MOD) % MOD) % MOD;}}}}}}int main(){scanf("%s", st + 1);int len = strlen(st + 1);int top = 0;for(int i = 1; i <= len; ++i) {if(st[i] == '(') {Stack[top++] = i;} else {match[Stack[--top]] = i;}}mst(dp, 0);Dfs(1, len);LL ans = 0;for(int i = 0; i < 3; ++i) {for(int j = 0; j < 3; ++j) {ans = ((ans + dp[1][len][i][j]) % MOD + MOD) % MOD;}}printf("%lld\n", ans);return 0;}



HDU 4283

题意:n个人排成一队,现在要让他们上场表演,每个人有一个Di值,如果第i个人第k个上场,那么这个人会产生怨气(k - 1) × Di,现在由一个小黑屋,是一个栈的结构,然后可以利用这个栈调整人的出场顺序,问产生的最少怨气值是多少

思路:dp[i][j],只考虑区间[i, j]人出场顺序时的最小怨气值,无论前面有多少人不管,i这个人可能第一个出场,也可能第j - i + 1个出场, 我们考虑i第k个出场,那么我们可以明确知道i后面k - 1个人一定是先上场的,然后i上场,剩余的再上场,就有子问题 dp[i + 1][i + k - 1] 这些人是前k - 1个上场,i第k个上场 产生的怨气(k - 1) * val[i], dp[i + k][j]这些人是后k个上场,但实际上dp[i + k][j]这个值只考虑了区间[i + k, j]的人上场顺序的最小值,但实际前面已经有k个人上场了,所以会产生(sum[j] - sum[i + k - 1]) * k的怨气值。

比如答案中第一个人是第k个上场,那么区间[2, k]的人是前k个上场的,这几个人上场顺序的最小值就是dp[2][k],也说明区间[k + 1, n]的人是再前k个人上场完再上场的但是dp[k + 1][n]仅仅只算了这(n - (k + 1) + 1)的上场顺序的最优怨气值,所以得加上k * (sum[n] - sum[k]).

#include <bits/stdc++.h>using namespace std;#define LL long long#define mst(a, b)memset(a, b, sizeof a)#define REP(i, x, n)for(int i = x; i <= n; ++i)const int INF = 2e9;const int qq = 2e5 + 10;int dp[205][205];int val[205], sum[205];int main(){int n, t, Cas = 0;scanf("%d", &t);while(t--) {scanf("%d", &n);sum[0] = 0;for(int i = 1; i <= n; ++i) {scanf("%d", val + i);sum[i] = sum[i - 1] + val[i];}mst(dp, 0);for(int i = 1; i <= n; ++i)for(int j = i + 1; j <= n; ++j)dp[i][j] = INF;for(int l = 1; l <= n - 1; ++l) {for(int i = 1; i <= n - 1; ++i) {int j = i + l;for(int k = 1; k <= j - i + 1; ++k) {dp[i][j] = min(dp[i][j], dp[i + 1][i + k - 1] + dp[i + k][j] + k * (sum[j] - sum[i + k - 1]) + val[i] * (k - 1));}}}printf("Case #%d: %d\n", ++Cas, dp[1][n]);}return 0;}



ZOJ 3541

题意:n个按钮,每个按钮有一个值Ti,代表在你按下这个按钮后Ti后这个按钮会弹上来,然后给出n个按钮的在坐标轴上的位置,问你能否有一种方案使得所有按钮都是down的状态。

思路:这题首先有一个结论,对于某个区间[i, j], 他一定是从左或者从右边开始按的,dp[i][j][0/1]代表区间[i, j] 从左向右按/右向左按所需的最短距离。

#include <cstdio>#include <cstring>#include <cmath>#include <iostream>#include <algorithm>#include <sstream>#include <map>#include <set>#include <vector>#include <utility>#include <queue>#include <stack>using namespace std;#define LL long long#define pb push_back#define mk make_pair#define pill pair<int, int>#define mst(a, b)memset(a, b, sizeof a)#define REP(i, x, n)for(int i = x; i <= n; ++i)const int MOD = 1e9 + 7;const int qq = 2e5 + 10;const int INF = 1 << 30;int dp[205][205][2], t[205], d[205], next[205][205][2];int main(){int n;while(scanf("%d", &n) != EOF) {for(int i = 1; i <= n; ++i) {scanf("%d", t + i);}for(int i = 1; i <= n; ++i) {scanf("%d", d + i);}mst(dp, 0);for(int  l = 1; l < n; ++l) {for(int i = 1; i + l <= n; ++i) {int j = i + l;dp[i][j][0] = min(dp[i + 1][j][0] + d[i + 1] - d[i], dp[i + 1][j][1] + d[j] - d[i]);next[i][j][0] = (dp[i + 1][j][0] + d[i + 1] - d[i] >= dp[i + 1][j][1] + d[j] - d[i]);if(dp[i][j][0] >= t[i] || dp[i][j][0] > INF)dp[i][j][0] = INF;dp[i][j][1] = min(dp[i][j - 1][1] + d[j] - d[j - 1], dp[i][j - 1][0] + d[j] - d[i]);next[i][j][1] = (dp[i][j - 1][1] + d[j] - d[j - 1] <= dp[i][j - 1][0] + d[j] - d[i]);if(dp[i][j][1] >= t[j] || dp[i][j][1] > INF)dp[i][j][1] = INF;}}/*for(int l = 1; l < n; ++l) {for(int i = 1; i + l <= n; ++i) {int j = i + l;printf("%d %d %d %d\n", i, j, dp[i][j][0], dp[i][j][1]);}}*/int l, r, m;if(dp[1][n][0] < INF) {l = 2, r = n, m = next[1][n][0];printf("1");} else if(dp[1][n][1] < INF){l = 1, r = n - 1, m = next[1][n][1];printf("%d", n);} else {printf("Mission Impossible");l = 1, r = -1;}while(l <= r) {if(m == 0) {printf(" %d", l);m = next[l][r][m];l++;} else {printf(" %d", r);m = next[l][r][m];r--;}}puts("");}return 0;}


原创粉丝点击