动态规划部分题目小结
来源:互联网 发布:标识设计软件 编辑:程序博客网 时间:2024/05/29 23:47
UVA10635 LCS转LIS
http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=19053
两个长度为p+1和q+1的序列A,B,序列中元素互不相同,且都是1到n^2的整数,求AB的LCS,将A中元素变为1到p+1,B中不在A中的元素直接删掉,问题转换为在B中求LIS
复杂度nlogn。
#include <bits/stdc++.h>using namespace std;const int maxn = 255*255;const int INF = 0x3f3f3f3f;int f[maxn],g[maxn],pos[maxn];int LIS(int n){ for(int i = 1; i <= n; ++i) g[i] = INF; int ans = 0; for(int i = 1; i <= n; ++i) { int k = lower_bound(g+1,g+1+n,f[i]) - g; ans = max(ans,k); g[k] = f[i]; } return ans;}int main(){ int T;scanf("%d",&T); for(int cas = 1; cas <= T; ++cas) { int n,p,q; scanf("%d%d%d",&n,&p,&q); memset(pos,0,sizeof(*pos)*(n*n+4)); for(int i = 0; i <= p; ++i) { int x; scanf("%d",&x); pos[x] = i+1; } int idx = 0; for(int i = 0; i <= q; ++i) { int x;scanf("%d",&x); if(pos[x]) { f[++idx] = pos[x]; } } //for(int i = 1; i <= idx; ++i) printf("%d%c",f[i]," \n"[i==idx]); int ans = LIS(idx); printf("Case %d: %d\n",cas,ans); } return 0;}
UVA 10891 区间型dp
http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=19309
一个数组,AB轮流取数,只能取连续的,只能从两端取,A先取,都采取最优策略,得分为取的数的和,求A得分-B得分。令d[i][j]表示先手取区间[i,j]能得到的最大价值,转移为
d[i,j] = min(sum[i,j] - min(d[i,k]),sum[i,j] - min(d[k,j]))可以用辅助数组加速转移。
#include <bits/stdc++.h>using namespace std;const int maxn = 100 + 10;const int INF = 0x3f3f3f3f;int dp[maxn][maxn],f[maxn][maxn],g[maxn][maxn];int s[maxn],a[maxn];int main(){ int n; while(scanf("%d",&n) == 1 && n) { s[0] = 0; for(int i = 1; i <= n; ++i) { scanf("%d",a+i); dp[i][i] = a[i]; f[i][i] = a[i]; g[i][i] = a[i]; s[i] = s[i-1] + a[i]; } for(int L = 1; L <= n; ++L) { for(int i = 1; i + L <= n; ++i) { int j = i + L; int m = 0; m = min(m,f[i+1][j]); m = min(m,g[i][j-1]); dp[i][j] = s[j] - s[i-1] - m; f[i][j] = min(dp[i][j],f[i+1][j]); g[i][j] = min(dp[i][j],g[i][j-1]); } } printf("%d\n",-s[n]+(dp[1][n]<<1)); } return 0;}
uva11825 状压dp
http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=20243
把p1,p2,p3,...pn,n个集合分成尽量多的组,使得组内集合的并为全集,状压dp可做,注意枚举子集的技巧。令d[s]表示s最多能分多少组,转移为d[s] = max(d[s],d[s^s0]+1)需保证s0里集合并集为全集。
#include <bits/stdc++.h>using namespace std;const int maxn = 16;int d[1<<maxn],p[maxn],cover[1<<maxn];void init(int n){ for(int i = 0; i < n; ++i) { p[i] = (1<<i); int cnt;scanf("%d",&cnt); for(;cnt;--cnt) { int x;scanf("%d",&x); p[i] |= (1<<x); } } for(int s = 0; s < 1<<n; ++s) { cover[s] = 0; for(int b = 0; b < n; ++b) { if((s>>b)&1) cover[s] |= p[b]; } }}int main(){ int n,cas = 0; while(scanf("%d",&n)==1&&n) { init(n); memset(d,0,sizeof d); int ALL = (1<<n)-1; for(int s = 1; s <= ALL; ++s) { for(int ns = s; ns; ns = (ns-1)&s) { if(cover[ns] == ALL) d[s] = max(d[s],d[s^ns]+1); } } printf("Case %d: %d\n",++cas,d[ALL]); } return 0;}
UVA 10859 树形dp
http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=19277
给一个n点,m条边的森林,选最少的点,使得所有的边都被覆盖,且被两个点覆盖的边尽可能的多,原问题等价于选最少的点和使最少的边只被一个点覆盖,令w = M*v1 + v2,v1是点数,v2是被一个点被覆盖的边,只要保证M > (maxv2-minv2)就能保证在v1最优的情况下在去优化v2。d[u][x]表示u为根的子树w的最小值,x表示u的父亲是否选择。选u的时候d[u][x] =M+sum(d[v][1])+test(u不是根),不选u,d[u][x] = sum(d[v][0]) + x,(不选u只有当u使根或者x等于1才可行)。
#include <bits/stdc++.h>using namespace std;const int maxn = 1e3 + 10;const int M = 2000;vector<int> G[maxn];int d[maxn][2];int dp(int u,int x,int fa){ int & ans = d[u][x]; if(ans != -1) return ans; ans = M;///u处放灯总是和法的 for(vector<int>::size_type i = 0; i < G[u].size(); ++i) { int v = G[u][i]; if(v != fa) ans +=dp(v,1,u); } if(fa != -1 && x == 0) ++ans;///u不是根,且父亲没放灯 if(fa==-1 || x == 1) {///u是根,或者u的父亲已放灯,u可以不放灯 int sum = 0; for(vector<int>::size_type i = 0; i < G[u].size(); ++i) { int v = G[u][i]; if(v != fa) sum += dp(v,0,u); } sum += x; ans = min(ans,sum); } return ans;}int main(){ int T;scanf("%d",&T); while(T--) { int n,m;scanf("%d%d",&n,&m); for(int i = 0; i <= n; ++i) G[i].clear(); memset(d,-1,sizeof d); for(int i = 0; i < m; ++i) { int u,v;scanf("%d%d",&u,&v); ++u,++v; G[u].push_back(v); G[v].push_back(u); } int ans = 0; for(int i = 1; i <= n; ++i) { if(d[i][0]==-1) ans += dp(i,0,-1); } printf("%d %d %d\n",ans/M,m-ans%M,ans % M); } return 0;}
LA3983 单调队列优化dp
http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=10159
n个垃圾,第i个坐标为(xi,yi,wi),wi为重量,机器人(初始在原点)按照编号从小到大的顺序将n个垃圾放到原点的垃圾桶处,每次可以选择一些垃圾放会垃圾桶,但总重量不得超过c,求机器人行走的最短距离,行走距离为曼哈顿距离。
令d[i] 表示将前i个垃圾放回原点的最小距离,则d[i] = min(d[j] + dist[j+1]+w[j+1,i]+dist[i]),复杂度为n^2,w[i,j]表示从i走到i+1,...j的曼哈顿距离,可以用单调队列优化一下,用sum[i]-sum[j]表示w[j,i],则转移变为d[i] = min(d[j]+dist[j+1]-w[j+1]) + dist[i]+w[i],单调队列维护d[j]+dist[j+1]-w[j+1]的最小值即可
#include <bits/stdc++.h>using namespace std;typedef long long ll;const int maxn = 1e5 + 10;int x[maxn],y[maxn];int total_dist[maxn],total_weight[maxn],dist2orgin[maxn];int d[maxn];int func(int i){ return d[i] - total_dist[i+1] + dist2orgin[i+1];}int main(){ int T;scanf("%d",&T); while(T--) { int n,c;scanf("%d%d",&c,&n); total_dist[0] = total_weight[0] = 0; x[0] = y[0] = 0; for(int i = 1; i <= n; ++i) { int w; scanf("%d%d%d",x+i,y+i,&w); dist2orgin[i] = abs(x[i]) + abs(y[i]); total_dist[i] = total_dist[i-1] + abs(x[i]-x[i-1]) + abs(y[i]-y[i-1]); total_weight[i] = total_weight[i-1] + w; } deque<int>Q; Q.push_front(0); for(int i = 1; i <= n; ++i) { while(!Q.empty() && total_weight[i] - total_weight[Q.front()] > c)Q.pop_front(); d[i] = func(Q.front()) + total_dist[i] + dist2orgin[i]; while(!Q.empty() && func(i) <= func(Q.back())) Q.pop_back(); Q.push_back(i); } printf("%d\n",d[n]); if(T)puts(""); } return 0;}
LA4794 状压dp
http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=11676
长宽为x,y的矩形能否划分成面积为a1,a2,...an的小矩形,状压dp,f[S][r][c]表示r×c的矩形能否划分成S集合的一些小矩形,限制r<=c,可以将状态表示成f[S][r]。
#include <bits/stdc++.h>using namespace std;typedef long long ll;const int maxn = 16;const int maxw = 100 + 4;int sum[1<<maxn],f[1<<maxn][maxw];int a[maxn];const char *str[] = {"No","Yes"};int bitcount(int S){ int tot = 0; for(; S ;S = (S-1) & S) ++tot; return tot;}int dp(int S,int x){ int & ans = f[S][x]; if(ans + 1) return ans; ans = 0; if(bitcount(S) == 1) return ans = 1; int y = sum[S]/x; for(int S0 = (S-1)&S; S0; S0 = (S0-1)&S) { int S1 = S ^ S0; if(sum[S0]%x==0&&dp(S0,min(x,sum[S0]/x))&&dp(S1,min(x,sum[S1]/x))) return ans = 1; if(sum[S0]%y==0&&dp(S0,min(y,sum[S0]/y))&&dp(S1,min(y,sum[S1]/y))) return ans = 1; } return ans = 0;}int main(){ int n; for(int cas = 1; ; ++cas) { scanf("%d",&n); if(!n) return 0; int x,y;scanf("%d%d",&x,&y); for(int i = 0; i < n; ++i) { scanf("%d",a + i); } int ALL = (1<<n) - 1; for(int s = 0; s <= ALL; ++s) { sum[s] = 0; for(int i = 0; i < n; ++i) { if((s>>i)&1) sum[s] += a[i]; } } int ans = 0; if(sum[ALL] == x*y && sum[ALL] % x == 0) { memset(f,-1,sizeof f); ans = dp(ALL,min(x,y)); } printf("Case %d: %s\n",cas,str[ans]); } return 0;}
- 动态规划部分题目小结
- 动态规划题目小结
- HDU动态规划部分小结
- 动态规划题目
- 动态规划小题目;
- 动态规划经典题目
- 动态规划经典题目
- POJ 动态规划题目
- 动态规划题目
- 动态规划题目思路
- 动态规划题目特性
- 动态规划题目总结
- |题目分类|动态规划
- 动态规划题目总结
- 动态规划题目集锦
- 动态规划题目整合
- 动态规划题目
- 动态规划题目整合
- Contest1040 - 第三届“图灵杯”NEUQ-ACM程序设计大赛(个人赛) E: 好学的coco
- 关于iOS的剪贴板基本知识
- POJ 1088 滑雪(记忆化搜索)
- Android Studio 版权信息编辑
- alterview使用
- 动态规划部分题目小结
- 【HTML5+css3】学习笔记之文档元素
- 【Oracle】——Navicat连接Oracle失败
- C语言链表的操作和讲解
- windows 10 关闭自动更新
- mbprogresshud简单使用
- Fragment的生命周期
- 1024. 科学计数法 (20)
- kindeditor初学