[kuangbin带你飞]专题十二 基础dp1
来源:互联网 发布:算法 pdf 高清 编辑:程序博客网 时间:2024/06/06 18:50
Max Sum Plus Plus
题意:
给你一个序列n个数组成,然后让你在里面找到m个字序列,让这不重叠的m个子序列的和最大。
思路:
这题比较水,所以不需要考虑太多数据相关的东西。直接二维dpOrz。
dp[i][j]:前j个序列,以i为结尾的,最大子序列和。dp[i][j]=max(dp[i - 1][j], dp[k][j - 1])+a[i]。即第i个数放在第j个序列里或者第j-1个序列里。显然k<i。显然我们可以拿一个数组来优化一下for1...k这个循环,做到O(1)。
#include <bits/stdc++.h>using namespace std;const int maxn = 1000000 + 5;int a[maxn];long long dp[maxn];long long maxx[maxn];int main(){ int m, n; while(~scanf("%d%d", &m, &n)) { memset(maxx, 0, sizeof(maxx)); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); long long temp; for(int i = 1; i <= m; i++) { temp = -1e18; for(int j = i; j <= n; j++) { dp[j] = max(dp[j - 1], maxx[j - 1]) + a[j]; maxx[j - 1] = temp; temp = max(temp, dp[j]); } } printf("%lld\n", temp); } return 0;}
Ignatius and the Princess IV
思路:
water
Monkey and Banana
题意:
把给定的长方体(不限)叠加在一起,叠加的条件是,上面一个长方体的长和宽都比下面长方体的长和宽短;求这些长方体能叠加的最高的高度.
思路:
其实这个问题可以转化成DAG上的一个最长路。长宽高三个维度,可以投射出六种(a, b ,c),当长宽满足其中要求时,连边,我们就可以得到一个DAG,然后再这个DAG上dp或者搜索出一条最长路就行了。
wa点:应该把所有的的点都初始化,而不只是初始化最大的,返利20 30 30。
#include <bits/stdc++.h>using namespace std;const int INF = 0x3f3f3f3f;const int maxn = 3e3 + 5;struct node{ int x, y, z; bool operator<(const node &other)const { if(x != other.x) return x > other.x; if(y != other.y) return y > other.y; return z < other.z; }};vector<node>vec;int dp[maxn];int main(){ int n, cas = 1; while(~scanf("%d", &n)) { if(n == 0) break; vec.clear(); for(int i = 0; i < n; i++) { int x, y, z; scanf("%d%d%d", &x, &y, &z); vec.push_back({x, y, z}); vec.push_back({y, x, z}); vec.push_back({x, z, y}); vec.push_back({z, x, y}); vec.push_back({z, y, x}); vec.push_back({y, z, x}); } sort(vec.begin(), vec.end()); for(int i = 0; i < vec.size(); i++) dp[i] = vec[i].z; for(int i = 0; i < vec.size(); i++) { for(int j = i + 1; j <vec.size();j++) { if(vec[j].x < vec[i].x && vec[j].y < vec[i].y) { dp[j] = max(dp[j], dp[i] + vec[j].z); } } } int ans = -INF; for(int i = 0; i < vec.size(); i++) ans = max(ans, dp[i]); printf("Case %d: maximum height = %d\n", cas++, ans); } return 0;}
Doing Homework
题意:
有n门课,每门课有截止时间和完成所需的时间,如果超过规定时间完成,每超过一天就会扣1分,问怎样安排做作业的顺序才能使得所扣的分最小。
思路:
我们可以看到数据范围是15,所以显然是一个状压dp。记录一下路径
#include <bits/stdc++.h>using namespace std;const int maxn = (1 << 16);const int INF = 0x3f3f3f3f;char name[20][105];int ddl[20], cost[20];int t[maxn], pre[maxn], dp[maxn];void show(int x){ if(x == 0) return; show(x - (1 << pre[x])); printf("%s\n", name[pre[x]]);}int main(){ int T; scanf("%d", &T); while(T--) { int n; scanf("%d", &n); for(int i = 0; i < n; i++) { scanf("%s%d%d", name[i], &ddl[i], &cost[i]); } for(int S = 1; S < (1 << n); S++) { dp[S] = INF; for(int i = n - 1; i >= 0; i--) { if((S & (1 << i)) == 0) continue; int past = S - (1 << i); int add = t[past] + cost[i] - ddl[i]; if(add < 0) add = 0; if(dp[past] + add < dp[S]) { dp[S] = dp[past] + add; t[S] = t[past] + cost[i]; pre[S] = i; } } } int o = (1 << n) - 1; printf("%d\n", dp[o]); show(o); } return 0;}
Super Jumping! Jumping! Jumping!
思路:
裸的的LIS
Piggy-Bank
题意:
给金币的面额和重量,求在满足一定重量时的最小价值。
思路:
完全背包
#include <bits/stdc++.h>using namespace std;const int maxn = 500 + 5;const int INF = 0x3f3f3f3f;typedef long long LL;int dp[10000 + 5];int val[maxn], w[maxn];int main(){ int T; scanf("%d", &T); while(T--) { memset(dp, INF, sizeof(dp)); int x, y, n; scanf("%d%d%d", &x, &y, &n); int V = y - x; for(int i = 1; i <= n; i++) { scanf("%d%d", &val[i], &w[i]); } dp[0] = 0; for(int i = 1; i <= n; i++) { for(int j = w[i]; j <= V; j++) { dp[j] = min(dp[j], dp[j - w[i]] + val[i]); } } if(dp[V] >= INF) { puts("This is impossible."); } else { printf("The minimum amount of money in the piggy-bank is %d.\n", dp[V]); } } return 0;}
免费馅饼
题意:
有一个数轴,从0到10,小明开始在5这个位置。现在天上开始掉馅饼,小明每次只能移动单位一的长度,求小明最多能接到多少馅饼。
思路:
简单dp。
#include <bits/stdc++.h>using namespace std;const int INF = 0x3f3f3f3f;const int maxn = 100000 + 5;int cake[15][maxn];int dp[15][maxn];int main(){ int n; while(~scanf("%d", &n)) { if(n == 0) break; memset(cake, 0, sizeof(cake)); memset(dp, -INF, sizeof(dp));//注意一个 初始位置在5的初始化 dp[5][0] = 0; int maxT = 0; for(int i = 0; i < n; i++) { int x, time; scanf("%d%d", &x, &time); cake[x][time]++; maxT = max(maxT, time); } for(int j = 1; j <= maxT; j++) { for(int i = 0; i <= 10; i++) { if(i - 1 >= 0) dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + cake[i][j]); if(i + 1 <= 10) dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + cake[i][j]); dp[i][j] = max(dp[i][j], dp[i][j - 1] + cake[i][j]); } } int ans = 0; for(int i = 0; i <= 10; i++) { ans = max(ans, dp[i][maxT]); } printf("%d\n", ans); } return 0;}
Tickets
题意:
有k个人要买票(你也在里面最后一位),每个人买票要花一个时间ai,或者两个相邻的人一起花费一个时间bi, 问你最少花费多少时间才能买到票。
思路:
简单dp,再开一个维度记录一下是第i次购买单人票和双人票的情况就行了。
#include <bits/stdc++.h>using namespace std;const int maxn = 2000 + 5;const int INF = 0x3f3f3f3f;int t[maxn], adj[maxn];int dp[maxn][2];int main(){ int T; scanf("%d", &T); while(T--) { memset(dp, INF, sizeof(dp)); int n; scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%d", &t[i]); } for(int i = 2; i <= n; i++) { scanf("%d", &adj[i]); } dp[0][0] = dp[0][1] = 0; for(int i = 1; i <= n; i++) { dp[i][0] = min(dp[i - 1][0], dp[i - 1][1]) + t[i]; if(i - 2 >= 0) { dp[i][1] = min(dp[i - 2][0], dp[i - 2][1]) + adj[i]; } } int ans = min(dp[n][0], dp[n][1]); int hh = 8, mm = 0, ss = 0; hh += ans / 3600; ans %= 3600; mm += ans / 60; ss += ans % 60; printf("%02d:%02d:%02d %s\n", hh, mm, ss, hh >= 12 ? "pm" : "am"); } return 0;}
最少拦截系统
思路:
LCS。
FatMouse's Speed
题意:
找到一个最多的老鼠序列,使得序列中的老鼠的体重满足递增,相应老鼠的速度满足递减。
思路:
按某一维排序以后,直接LIS就行了。但是这题窝一定要吐槽!!!! 窝日 为什么按w升序就行,降序就不行exome?
#include <bits/stdc++.h>using namespace std;const int maxn = 1e3 + 5;const int INF = 0x3f3f3f3f;typedef long long LL;typedef pair<int, int>pii;int path[maxn];int dp[maxn];struct node{ int s, w, id; bool operator < (const node &other)const { if(w != other.w) return w < other.w; return s > other.s; }}m[maxn];;int main(){ int s, w, cnt = 0; while(~scanf("%d%d", &s, &w)) { m[cnt] = {s, w, cnt + 1}; cnt++; } sort(m, m + cnt); memset(path, -1, sizeof(path)); for(int i = 1; i < cnt; i++) dp[i] = 1; for(int i = 1; i < cnt; i++) { for(int j = 0; j < i; j++) { if(m[j].w < m[i].w && m[j].s > m[i].s && dp[i] < dp[j] + 1) { dp[i] = dp[j] + 1; path[m[i].id] = m[j].id; } } } int ans = dp[0], pos = m[0].id; for(int i = 0; i < cnt; i++) { if(dp[i] > ans) { ans = dp[i]; pos = m[i].id; } } printf("%d\n", ans); queue<int>que; que.push(pos); while(path[pos] != -1) { que.push(path[pos]); pos = path[pos]; } while(que.size()) { printf("%d\n", que.front()); que.pop(); } return 0;}
Common Subsequence
思路:
裸的LCS。
Help Jimmy
题意:
场景中包括多个长度和高度各不相同的平台。地面是最低的平台,高度为零,长度无限。 Jimmy老鼠在时刻0从高于所有平台的某处开始下落,它的下落速度始终为1米/秒。当Jimmy落到某个平台上时,游戏者选择让它向左还是向右跑,它跑动的速度也是1米/秒。当Jimmy跑到平台的边缘时,开始继续下落。Jimmy每次下落的高度不能超过MAX米,不然就会摔死,游戏也会结束。 设计一个程序,计算Jimmy到底地面时可能的最早时间。
思路:
二维数组,dp[i][j]记录第i个平台,的左或者右到达地面的最早时间。然后怎么找到,你能够跳跃到的状态呢,显然是在该平台的下方,满足该平台的左/右边,在跳跃到的平台的左右界之内,所以我们按照高度排个序,直接O(n)的扫。
#include <iostream>#include <cstdio>#include <algorithm>using namespace std;const int maxn = 1e3 + 5;const int INF = 0x3f3f3f3f;struct node{ int l, r, h; bool operator <(const node &other)const { if(h != other.h) return h < other.h; return l <other.l; }}a[maxn];int dp[maxn][2];int main(){ int T; scanf("%d", &T); while(T--) { int sx, sy, maxh, n; scanf("%d%d%d%d", &n , &sx, &sy, &maxh); for(int i = 0; i < n; i++) { int l, r, h; scanf("%d%d%d", &l, &r, &h); a[i] = {l, r, h}; } a[n++] = {sx, sx, sy}; a[n++] = {-INF, INF, 0}; sort(a, a + n); for(int i = 1; i < n; i++) { dp[i][0] = dp[i][1] = INF; for(int j = i - 1; j >= 0; j--) { if(a[i].h - a[j].h > maxh) continue; if(a[j].l <= a[i].l && a[j].r >= a[i].l) { dp[i][0] = a[i].h - a[j].h; if(j != 0) dp[i][0] += min(dp[j][0] + a[i].l-a[j].l, dp[j][1] + a[j].r-a[i].l); break; } } for(int j = i - 1; j >= 0; j --) { if(a[i].h - a[j].h > maxh) continue; if(a[j].l <= a[i].r && a[j].r >= a[i].r) { dp[i][1] = a[i].h - a[j].h; if(j != 0) dp[i][1] += min(dp[j][0] + a[i].r-a[j].l, dp[j][1] + a[j].r-a[i].r); break; } } } printf("%d\n", min(dp[n - 1][0], dp[n - 1][1])); } return 0;}
Longest Ordered Subsequence
思路:
LIS。
Treats for the Cows
题意:
给一行n个数,每次可以取出行首或者行末的数,如果第ai是第i次取出的,可以得到ai*i的收益,求最大的总收益。
思路:
显然是一个经典的区间dp问题。dp[i][j]在第(n-j+i)次,剩余第i个到第j个物品未取的时候的总收益。这样听起来悬乎,其实可以直接考虑,当前剩余第i个到第j个东西。那么你这是第几次取,肯定已知吧,那么一定是剩余第i到第j-1个物品的状态或者剩余第i+1到第j个物品的状态转移来的。
#include <cstdio>#include <iostream>#include <algorithm>using namespace std;const int maxn = 2000 + 5;typedef long long LL;int val[maxn];LL dp[maxn][maxn];int main(){ int n; scanf("%d", &n); for(int i = 0; i < n; i++) { scanf("%d", &val[i]); } for(int i = 0; i < n; i++) { dp[i][i] = 1LL * n * val[i]; } for(int i = n - 1; i >= 0; i--) { for(int j = i; j < n; j++) { if(j - 1 >= i) dp[i][j] = max(dp[i][j], dp[i][j - 1] + 1LL * val[j] * (n - j + i)); if(i + 1 <= j) dp[i][j] = max(dp[i][j], dp[i + 1][j] + 1LL * val[i] * (n - j + i)); } } printf("%lld\n", dp[0][n - 1]); return 0;}
FatMouse and Cheese
题意:
在n*n的格子上,每个点各有若干块奶酪,胖老鼠从左上角出发,每次最多走k步(只能直走),且下一点必须比这一点的奶酪多,问最多能吃到多少块奶酪。n的范围是100。
思路:
记忆化搜索。
#include <bits/stdc++.h>using namespace std;const int maxn = 100 + 5;int ma[maxn][maxn];int dp[maxn][maxn];int n, k, ans;int dx[] = {0, 0, -1, 1};int dy[] = {1, -1, 0, 0};#define Inside(x, y) 0 <= x && x < n && 0 <= y && y < nint dfs(int x, int y){ if(dp[x][y]) return dp[x][y]; int ret = 0;//如果是K*K的双重循环会T for(int kk = 1; kk <= k; kk++) { for(int dir = 0; dir < 4; dir++) { int xx = dx[dir] * kk, yy = dy[dir] * kk; if(abs(xx) + abs(yy) > k) continue; if(xx == 0 && yy == 0) continue; int fx = x + xx, fy = y + yy; if(Inside(fx, fy) && ma[fx][fy] > ma[x][y]) { int temp = dfs(fx, fy); if(temp > ret) ret = temp; } } } return dp[x][y] = ret + ma[x][y];}int main(){ while(~scanf("%d%d", &n, &k)) { if(n == -1 && k == -1) break; memset(dp, 0, sizeof(dp)); for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { scanf("%d", &ma[i][j]); } } printf("%d\n", dfs(0, 0)); } return 0;}
Phalanx
题意:
给定矩阵,求符合对称矩阵的最大子矩阵的宽度。 这里的对称矩阵是以左下至右上为轴的。矩阵大小1000.
思路:
直接暴力就行了。
#include <bits/stdc++.h>using namespace std;const int maxn = 1e3 + 5;char ma[maxn][maxn];int dp[maxn][maxn];int main(){ int n; while(~scanf("%d", &n)) { if(n == 0) break; memset(dp, 0, sizeof(dp)); for(int i = 1; i <= n; i++) { scanf("%s", ma[i] + 1); reverse(ma[i] + 1, ma[i] + 1 + n); } int ans = 1; for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { dp[i][j] = 1; int p1 = i, p2= j; while(p1 >= 1 && p2 >= 1 && ma[p1][j] == ma[i][p2]) { p1--, p2--; } int cnt = i - p1; if(cnt >= dp[i - 1][j - 1] + 1) dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1); else dp[i][j] = max(dp[i][j], cnt); ans = max(ans, dp[i][j]); } } printf("%d\n", ans); } return 0;}
Milking Time
题意:
奶牛为自己规划下面n时间内的产奶,m个时间段,每个段有a,b,c表示从a时到b时共可产奶c。 挤奶工每次挤奶必须挤完完整的时间段,且每次挤完需要休息r时,求最终可获得的牛奶最大值。
思路:
简单dp。
#include <cstdio>#include <algorithm>#include <iostream>using namespace std;const int maxn = 1000 + 5;struct node{ int s, e, w; bool operator <(const node &other)const { if(s != other.s) return s < other.s; return e < other.e; }}cow[maxn];int dp[maxn];int main(){ int n, m, R; scanf("%d%d%d", &n, &m, &R); for(int i = 1; i <= m; i++) { int s, e, w; scanf("%d%d%d", &s, &e, &w); cow[i] = {s, e + R, w}; } sort(cow + 1, cow + 1+ m); int ans = 0; for(int i = 1; i <= m; i++) { if(cow[i].s > n) break; for(int j = 0; j < i; j++) { if(cow[j].e <= cow[i].s) { dp[i] = max(dp[i], dp[j] + cow[i].w); ans = max(ans, dp[i]); } } } printf("%d\n", ans); return 0;}
Making the Grade
题意:
就是农夫要修一条路,现在要求这条路要么就是上升的,要么就是下降的,总代价为∑|a[i]-b[i]|,求代价最低的修路方案, (0 ≤ β≤ 1,000,000,000) , (1 ≤ N ≤ 2,000)
思路:
dp[i][j]:前i个位置,最大元素是原来的路中的第j大的元素的时候,代价最低的修路方案。
#include <cstdio>#include <algorithm>using namespace std;const int maxn = 2e3 + 5;int a[maxn], b[maxn];int dp[maxn][maxn];int solve(int a[], int b[], int n){ for(int i = 1; i <= n; i++) { int rec = 0x3f3f3f3f; for(int j = 1; j <= n; j++) { rec = min(rec, dp[i - 1][j]); dp[i][j] = rec + abs(a[i] - b[j]); } } return *min_element(dp[n] + 1, dp[n] + 1+ n);}int main(){ int n; scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%d", &a[i]); b[i] = a[i]; } sort(b + 1, b + 1 + n); int ans1 = solve(a, b, n); for(int i = 1; i <= n; i++) b[i] = -b[i]; sort(b + 1, b + 1 + n); int ans2 = solve(a, b, n); printf("%d\n", min(ans1, ans2)); return 0;}
- 【 题集 】 【kuangbin带你飞】专题十二 基础DP1
- kuangbin带你飞 专题十二 基础DP1
- [kuangbin带你飞]专题十二 基础DP1 O POJ3186
- [kuangbin带你飞]专题十二 基础DP1
- [kuangbin带你飞]专题十二 基础DP1
- [kuangbin带你飞]专题十二 基础DP1 -B
- [kuangbin带你飞]专题十二 基础DP1 C
- [kuangbin带你飞]专题十二 基础dp1
- [kuangbin带你飞]专题十二 基础DP1
- [kuangbin带你飞]专题十二 基础DP1 G - 免费馅饼(HDU 1176)
- [kuangbin带你飞]专题十二 基础DP1 G - 免费馅饼
- [kuangbin带你飞]专题十二 基础DP1 I - 最少拦截系统
- [kuangbin带你飞]专题十二 基础DP1 B - Ignatius and the Princess IV
- [kuangbin带你飞]专题十二 基础DP1 A HDU 1024
- [kuangbin带你飞]专题十二 基础DP1 B HDU 1029
- [kuangbin带你飞]专题十二 基础DP1 C HDU 1069
- [kuangbin带你飞]专题十二 基础DP1 D HDU 1074
- [kuangbin带你飞]专题十二 基础DP1 E HDU 1087
- 如何做到优化引擎搜索SEO之第四篇:谷歌分析(Google Analytics)
- java-DAO模式实现数据库增,删,改,查和传统模式实现数据库增,删,改,查对比-JDBC
- 海量数据存储管理
- getline()详解
- canvas插入图片和画文字
- [kuangbin带你飞]专题十二 基础dp1
- SDUT 3919 Special Judge Ⅰ
- C#——Marshal.StructureToPtr方法简介
- 二叉树的链式结构
- linux安装rabbitMq
- java类加载器
- cocos2dx lua编译打包安卓报的错误汇总
- git使用笔记
- Error from server: error dialing backend: dial tcp: lookup xxx on 114.114.114.114:53: no such