Typesetting math: 100%

Loading

[kuangbin带你飞]专题二十二 区间DP

[kuangbin带你飞]专题二十二 区间DP

ZOJ 3537 Cake

判断闭包+最优三角划分

注意是用求出的闭包来算转移

#include <bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; int m,f[400][400],dp[400][400]; struct Point{ int x,y; Point(){} Point(int xx,int yy):x(xx),y(yy){} void read(){ scanf("%d%d",&x,&y); } bool operator < (const Point &other)const{ if(x == other.x) return y < other.y; return x < other.x; } Point operator - (const Point &other)const{ return Point(x - other.x,y - other.y); } }; Point p[400],ch[400]; int n; double Cross(Point A,Point B){ return A.x * B.y - A.y * B.x; } int ConvexHull(){ sort(p,p+n); int cnt = 0; for(int i = 0; i < n; i++){ while(cnt > 1 && Cross(ch[cnt-1] - ch[cnt-2],p[i] - ch[cnt-2]) <= 0) cnt--; ch[cnt++] = p[i]; } int k = cnt; for(int i = n-2; i >= 0; i--){ while(cnt > k && Cross(ch[cnt-1] - ch[cnt-2],p[i] - ch[cnt-2]) <= 0) cnt--; ch[cnt++] = p[i]; } if(n > 1) cnt--; return cnt; } int calc(Point a,Point b){ return (abs(a.x + b.x) * abs(a.y + b.y)) % m; } int main(){ while(scanf("%d%d",&n,&m) != EOF){ memset(p,0,sizeof(p)); memset(ch,0,sizeof(ch)); memset(f,0,sizeof(f)); for(int i = 0; i < n; i++){ p[i].read(); } if(n == 3){ puts("0"); continue; } if(ConvexHull() < n){ printf("I can't cut.\n"); } else{ for(int i = 0; i < n; i++){ for(int j = i+2; j < n; j++){ f[i][j] = f[j][i] = calc(ch[i],ch[j]); } } for(int i = 0; i < n; i++){ for(int j = 0; j < n; j++){ dp[i][j] = INF; } dp[i][(i+1)%n] = 0; } for (int len = 3; len <= n; len++) { for (int i = 0; i + len - 1 < n; i++) { int j = i + len - 1; for (int k = i + 1; k <= j - 1; k++) { dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j]+f[i][k]+f[k][j]); } } } printf("%d\n",dp[0][n-1]); } } return 0; }

LightOJ 1422 Halloween Costumes

大意:

n个宴会,对于每一个宴会,都要穿一种礼服,礼服可以套着穿,但是脱了的不能再用,参加宴会必须按顺序来,从第一个到第N个,问参加这些宴会最少需要几件礼服

思路:

当c[j-1] = c[j] 时显然有dp[ i ] [ j ] = dp[ i ] [j-1]

更新是更新当第k场舞会衣服与第j场衣服一致时,假设第k场衣服一直穿着

#include <bits/stdc++.h> using namespace std; const int N = 1e2 + 5; typedef long long LL; int t, a[N], dp[N][N], cases = 0; int main() { cin >> t; while (t--) { cases++; memset(dp, 0x3f, sizeof dp); int n; cin >> n; for (int i = 0; i < n; i++) { cin >> a[i]; } for (int i = 0; i < n; i++) { dp[i][i] = 1; } for (int len = 2; len <= n; len++) { for (int i = 0; i + len - 1 < n; i++) { int j = i + len - 1; if (a[j] == a[j - 1]) dp[i][j] = dp[i][j - 1]; else dp[i][j] = dp[i][j - 1] + 1; for (int k = i; k < j; k++) { if (a[k] == a[j]) { dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j - 1]); } } } } printf("Case %d: %d\n", cases, dp[0][n - 1]); } return 0; }

POJ 2955 Brackets

大意:

给出一个字符串,问满足括号匹配的最长区间是多少

满足括号匹配的有:

(), [], (()), ()[], ()[()]

思路:

s[i]==s[j]s[i]==s[j]时,dp[i][j]==dp[i+1][j1]+2dp[i][j]==dp[i+1][j1]+2

然后直接转移就行

#include <math.h> #include <stdio.h> #include <string.h> #include <algorithm> #include <cstdio> #include <iostream> #include <queue> #include <stack> #include <vector> using namespace std; const int N = 1e2 + 5; typedef long long LL; int dp[N][N]; int main() { string s; while (cin >> s) { if (s == "end") break; int n = s.size(); memset(dp, 0, sizeof dp); int res = 0; for (int len = 2; len <= n; len++) { for (int i = 0; i + len - 1 < n; i++) { int j = i + len - 1; if ((s[i] == '(' && s[j] == ')') || (s[i] == '[' && s[j] == ']')) dp[i][j] = dp[i + 1][j - 1] + 2; for (int k = i; k < j; k++) { dp[i][j] = max(dp[i][k] + dp[k + 1][j], dp[i][j]); } res = max(res, dp[i][j]); } } cout << res << endl; } return 0; }

CodeForces 149D Coloring Brackets

大意:

给出一个能被匹配的括号字符串,现在对这些括号进行涂色,要求满足:

对于每一对配对的括号,有且只有一个括号染色

相邻的括号,如果都染色了,颜色不能相同

每个括号要么不染色,要么染红色,要么染绿色

思路:

首先利用栈对括号进行匹配

利用dfs写区间dp,枚举两侧的颜色,然后进行转移

如果当前区间的左右端点配对,那么直接进入下一层区间

否则枚举左端点对应的配对的右括号,枚举全部的颜色即可

#include <bits/stdc++.h> using namespace std; const int N = 7e2 + 5; typedef long long LL; LL dp[N][N][5][5]; string s; const LL mod = 1e9 + 7; int n, ne[N]; stack<int> st; LL dfs(int l, int r, int c1, int c2) { LL res = 0; if (l >= r) return dp[l][r][c1][c2] = 0LL; if (dp[l][r][c1][c2] != -1) return dp[l][r][c1][c2]; if (ne[l] == r) { //遇到这个区间左右端点恰好匹配,那么左右端点的方案已经固定了 if ((c1 == 0 && c2 != 0) || (c2 == 0 && c1 != 0)) { //当前匹配必须合法 if (l + 1 == r) return dp[l][r][c1][c2] = 1LL; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (((i == 0) || (c1 == 0) || (i != c1)) && ((j == 0) || (c2 == 0) || (j != c2))) { res = (res + dfs(l + 1, r - 1, i, j)) % mod; } } } } } else { int k = ne[l]; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if ((i == 0) || (j == 0) || (i != j)) { res = (res + dfs(l, k, c1, i) * dfs(k + 1, r, j, c2) % mod) %mod; } } } } return dp[l][r][c1][c2] = res; } int main() { cin >> s; memset(dp, -1, sizeof dp); for (int i = 0; i < s.size(); i++) { if (s[i] == '(') st.push(i); else { ne[st.top()] = i; st.pop(); } } n = s.size(); LL res = 0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { res = (res + dfs(0, n - 1, i, j)) % mod; } } cout << res << endl; return 0; }

POJ 1651 Multiplication Puzzle

大意:

给出n个数,每次从没有取出的数中取出一个数,花费是这个数和两边的还没有取出来的数的乘积,最左边和最右边的数不能取,问最后花费最小是多少

思路:

枚举中点转移即可,注意转移的区间是 i,k 和 k , j,不是i,k 和 k+1 , j

区间dp就是要认真分析区间边界啊...

#include <math.h> #include <stdio.h> #include <string.h> #include <algorithm> #include <cstdio> #include <iostream> #include <queue> #include <stack> #include <vector> using namespace std; const int N = 1e2 + 5; typedef long long LL; int n; LL a[N], dp[N][N]; int main() { cin >> n; for (int i = 0; i < n; i++) cin >> a[i]; memset(dp, 0x3f, sizeof dp); for (int i = 0; i < n - 1; i++) dp[i][i + 1] = 0; for (int len = 3; len <= n; len++) { for (int i = 0; i + len - 1 < n; i++) { int j = i + len - 1; for (int k = i + 1; k < j; k++) { dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j] + a[i] * a[k] * a[j]); } } } cout << dp[0][n - 1] << endl; return 0; }

ZOJ 3469 Food Delivery

大意:

在X-轴上有一个餐厅,以及有n个点要送外卖。餐厅坐标为x,工人的速度为v1v1,也就是 1/v 米每分钟。给出n个点的x坐标和b,外卖没送到,客户就会不愉快,每一分钟的不愉快指数增加b。问怎么样的送货策略,使得所有客户的总不愉悦指数和最小。

思路:

可以推出,当需要送区间l到r的两端的外卖时,必然是从区间l到r-1或者l+1到r转移过来,也就是说区间内部的点已经送过了,不然他会先送区间内部的点

但是员工肯定要么是在区间左端点要么是在区间右端点,所以需要多开一维维护是在左边还是在右边

#include <bits/stdc++.h> using namespace std; const int N = 1e3 + 5; typedef long long LL; int n, v, x; struct node { int pos, b; } a[N]; int dp[N][N][2], sum[N]; bool cmp(node a, node b) { return a.pos < b.pos; } int main() { while (scanf("%d%d%d", &n, &v, &x) != EOF) { for (int i = 1; i <= n; i++) scanf("%d%d", &a[i].pos, &a[i].b); n++; a[n].pos = x, a[n].b = 0; sort(a+1, a + n + 1,cmp); int pos = 0; for (int i = 1; i <= n; i++) { if (a[i].pos == x) { pos = i; break; } } for (int i = 1; i <= n; i++) { sum[i] = sum[i - 1] + a[i].b; } memset(dp, 0x3f, sizeof dp); for (int i = pos; i >=1 ; i--) { for (int j = pos; j <= n; j++) { if (i == j) { dp[i][j][0] = dp[i][j][1] = 0; continue; } int mid = sum[i - 1] - sum[0] + sum[n] - sum[j]; dp[i][j][0] = min(dp[i][j][0],dp[i + 1][j][0] + (mid+a[i].b) * (a[i + 1].pos - a[i].pos)); dp[i][j][0] = min(dp[i][j][0],dp[i + 1][j][1] + (mid+a[i].b) * (a[j].pos - a[i].pos)); dp[i][j][1] = min(dp[i][j][1],dp[i][j-1][0] + (mid+a[j].b) * (a[j].pos - a[i].pos)); dp[i][j][1] = min(dp[i][j][1],dp[i][j-1][1] + (mid+a[j].b) * (a[j].pos - a[j-1].pos)); } } cout << min(dp[1][n][0], dp[1][n][1]) * v << endl; } return 0; }

HDU 4283 You Are the One

大意:

有n个人,v [ i ] 表示每个人的愤怒值,现在他们依次上台表演,如果i是第k个上台,那么他的愤怒值为(k-1)* v[i] 现在有一个栈,可以让一些人进栈,让后面的人先上台表演,这样可以改变部分顺序,求所有人最小的愤怒值和

思路:

我们考虑区间[l,r],如果第一个人是该区间中第k个上场的,那么区间[l+1,r]中的人应当先于第一个上场,区间[l+k+1,r]中的人必定要在第一个人之后上场,这样一个区间就被划分成了两个区间。

dp[ i + 1 ] [ i + k - 1]表示前 k - 1个人的沮丧值,a[i] * (k-1)表示第i个人的沮丧值,而i+k到j的这些人由于出场位置都增加了K,所以总的沮丧值增加了k * (sum[j] - sum[i+k-1])

#include <bits/stdc++.h> using namespace std; const int N = 1e2 + 5; typedef long long LL; int n, dp[N][N], t, a[N], sum[N], cases; int main() { cin >> t; while (t--) { cases++; cin >> n; for (int i = 1; i <= n; i++) cin >> a[i], sum[i] = sum[i - 1] + a[i]; memset(dp, 0, sizeof dp); for (int len = 1; len <= n; len++) { for (int i = 1; i + len - 1 <= n; i++) { int j = i + len - 1; dp[i][j] = 0x3f3f3f3f; if (len==1) { dp[i][j] = 0; continue; } for (int k = 1; k <= len; k++) { dp[i][j] = min(dp[i][j], dp[i + 1][i + k - 1] + a[i] * (k - 1) +dp[i+k][j]+ k * (sum[j] - sum[i + k-1])); } } } printf("Case #%d: %d\n", cases, dp[1][n]); } return 0; }

HDU 2476 String painter

大意:

给出两个字符串s1,s2。对于每次操作可以将 s1 串中的任意一个子段变成另一个字符。问最少需要多少步操作能将s1串变为s2串。

思路:

这个题先考虑怎么由空串转化s2,
f [ i ] [ j ] 表示从空串到s2最少的次数,
则有f[ i ] [ j ] = s [ i + 1 ] [ j ] +1,
若[i + 1, j ]存在一个 k ,使s2[ i ]==s2[ k ],则f[ i ] [ j ]=min{f[i+1] [k ]+f[ k+1] [ j ] },
也就是说i和k用同一次变换

然后再考虑把s1刷成s2的代价
设sum[i]表示把s1[1,i]刷成s2[1,i]的次数
当s1[i]==s2[i]时,可以不刷,显然sum[i]=sum[i-1]
否则,在区间内找最小次数sum[i]=min(sum[j] + f [j+1] [i])

#include <bits/stdc++.h> using namespace std; const int N = 1e3 + 10; int n, m; int f[N][N], sum[N]; char s[N], t[N]; int main() { while (cin >> s) { cin >> t; memset(f, 0, sizeof f); memset(sum, 0, sizeof sum); int len = strlen(s); for (int i = 0; i < len; ++i) f[i][i] = 1; for (int i = 0; i < len; ++i) for (int j = i - 1; j >= 0; --j) { f[j][i] = f[j + 1][i] + 1; for (int k = j + 1; k <= i; ++k) if (t[j] == t[k]) f[j][i] = min(f[j][i], f[j + 1][k] + f[k + 1][i]); } for (int i = 0; i < len; ++i) sum[i] = f[0][i]; if (s[0] == t[0]) sum[0] = 0; for (int i = 1; i < len; ++i) { if (s[i] == t[i]) sum[i] = min(sum[i], sum[i - 1]); else for (int j = 0; j < i; ++j) sum[i] = min(sum[i], sum[j] + f[j + 1][i]); } cout << sum[len - 1] << endl; } }

__EOF__

本文作者dyhaohaoxuexi
本文链接https://www.cnblogs.com/dyhaohaoxuexi/p/14435141.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   WinterFa1L  阅读(46)  评论(0编辑  收藏  举报
编辑推荐:
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
阅读排行:
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· 程序员常用高效实用工具推荐,办公效率提升利器!
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 【译】WinForms:分析一下(我用 Visual Basic 写的)
点击右上角即可分享
微信分享提示