首页 > 学院 > 开发设计 > 正文

[kuangbin带你飞]专题十二 基础DP1

2019-11-10 19:00:46
字体:
来源:转载
供稿:网友

A - Max Sum Plus Plus

求一个数列中取m个不相交子序列所取得的最大值。 dp[i][j]表示取i个子序列且最后一个序列的末尾为j时取得的最大值,所以对于任意一个j只有两种情况,单独形成一个新序列的开头或者加入上一个序列。

#include<stdio.h>#include<algorithm>#include<iostream>using namespace std;#define MAXN 1000000#define INF 0x7fffffffint dp[MAXN + 10];int mmax[MAXN + 10];int a[MAXN + 10];int main(){ int n, m; int i, j, mmmax; while (scanf("%d%d", &m, &n) != EOF) { for (i = 1; i <= n; i++) { scanf("%d", &a[i]); mmax[i] = 0; dp[i] = 0; } mmax[0] = dp[0] = 0; for (int i = 1; i <= m; i++){ mmmax = -INF; for (int j = i; j <= n; j++){ dp[j] = max(dp[j - 1] + a[j], mmax[j - 1] + a[j]);//a[j]包含在dp[j-1]的最后一个连续段中或单独成段 mmax[j - 1] = mmmax;//dp转移所用到的mmax[j-1]是上一轮得到的,所以这里每次求的是上一个的防止覆盖 mmmax = max(mmmax, dp[j]); } } PRintf("%d/n", mmmax); } return 0;}

B - Ignatius and the Princess IV

数列中有一个数出现的次数大于数列总数数量的一半,在编程之美中看过,sort一下之后数列正中间处的数一定是那个数。

#include<iostream>#include<string.h>#include<algorithm>#include<queue>#include<set>#include<vector>using namespace std;int n;int num[1000000];int main(){ while (~scanf("%d", &n)){ for (int i = 0; i < n; i++){ scanf("%d", &num[i]); } sort(num, num + n); printf("%d/n", num[n / 2]); } return 0;}

C - Monkey and Banana

叠箱子,叠在上面的箱子必须长和宽都比下面的小,dp状态为以该箱子为底时可以得到的最高高度。对于箱子a和b,若a的长宽都大于b,则a.dp=max(b.dp+a.h,a.dp);

#include<iostream>#include<string.h>#include<algorithm>#include<vector>using namespace std;int n;struct p{ int x, y, h,dp;}data[100];bool cmp(p a, p b){ if (a.x == b.x){ return a.y > b.y; } return a.x > b.x;}int main(){ int a, b, c; int t = 0; while (~scanf("%d", &n),n){ t++; for (int i = 0; i < n; i++){ scanf("%d%d%d",&a,&b,&c ); if (a>b){ swap(a, b); } if (a > c){ swap(a, c); } if (b > c){ swap(b, c); } data[i * 3].x = a, data[i * 3].y = b, data[i * 3].h=data[i*3].dp= c; data[i * 3+1].x = a, data[i * 3+1].y = c, data[i * 3+1].h =data[i*3+1].dp= b; data[i * 3+2].x = b, data[i * 3+2].y = c, data[i * 3+2].h =data[i*3+2].dp= a; } int k = 3 * n; int ans = 0; sort(data, data + k,cmp); for (int i = 0; i < k; i++){ for (int j = 0; j < i; j++){ if (data[i].x < data[j].x&&data[i].y < data[j].y){ data[i].dp = max(data[i].dp, data[j].dp + data[i].h); ans = max(ans, data[i].dp); } } } printf("Case %d: maximum height = %d/n",t,ans ); } return 0;}

D - Doing Homework

之前写过的了。

E - Super Jumping! Jumping! Jumping!

#include<iostream>#include<stdio.h>#include<string>#include<string.h>#include<algorithm>#include<vector>using namespace std;int num[1005];long long dp[1005];int n;int main(){ while (scanf("%d", &n), n != 0){ for (int i = 1; i <= n; i++){ scanf("%d", &num[i]); } memset(dp, 0, sizeof(dp)); for (int i = 1; i <= n; i++){ for (int j = 0; j < i; j++){ if (num[j] < num[i]){ dp[i] = max(dp[j] + num[i], dp[i]); } } } long long ans = 0; for (int i = 1; i <= n; i++){ ans = max(ans, dp[i]); } printf("%lld/n", ans); } return 0;}

F - Piggy-Bank

经典的dp题,但是得刚刚好才行,所以dp初始化为-1。

#include<iostream>#include<stdio.h>#include<string>#include<string.h>#include<algorithm>#include<vector>using namespace std;int e, f,n,total;int v[505];int need[505];int dp[10005];int main(){ int t; scanf("%d", &t); while (t--){ scanf("%d%d", &e, &f); total = f - e; scanf("%d", &n); memset(dp, 0x3f, sizeof(dp)); for (int i = 0; i < n; i++){ scanf("%d %d", &v[i], &need[i]); } dp[0] = 0; for (int i = 0; i < n; i++){ for (int j = need[i]; j <= total; j++){ dp[j] = min(dp[j], dp[j - need[i]] + v[i]); } } if (dp[total] == 0x3f3f3f3f){ printf("This is impossible./n"); } else{ printf("The minimum amount of money in the piggy-bank is %d./n", dp[total]); } } return 0;}

G - 免费馅饼

dp[j][i]为第i秒在j位置时可以得到馅饼得最大值,dp[j][i] = max(max(dp[j - 1][i - 1], dp[j + 1][i - 1]), dp[j][i - 1])+v[j][i];

#include<iostream>#include<string.h>#include<algorithm>#include<queue>#include<set>#include<string>#include<vector>using namespace std;const int maxt = 100005;int n,t;int v[13][maxt];int dp[13][maxt];int main(){ int a, b; while (scanf("%d", &n), n != 0){ memset(v, 0, sizeof(v)); t = 0; for (int i = 0; i < n; i++){ scanf("%d%d", &a, &b); v[a+1][b]++; t = max(t, b); } for (int i = 0; i < 13; i++){ for (int j = 0; j <= t; j++){ dp[i][j] = -10000; } } dp[6][0] = 0; for (int i = 1; i <= t; i++){ for (int j = 1; j <= 11; j++){ dp[j][i] = max(max(dp[j - 1][i - 1], dp[j + 1][i - 1]), dp[j][i - 1])+v[j][i]; } } int ans = 0; for (int i = 1; i < 12; i++){ ans = max(ans, dp[i][t]); } printf("%d/n", ans); } return 0;}

H - Tickets

卖票可以单个人单个人卖,也可以两个一起卖,已知每个人单人所用的时间和相邻两个人所需的时间,求最少用时,dp[i] = min(dp[i - 1] + single[i], dp[i - 2] + doubl[i - 1]);

#include<iostream>#include<string.h>#include<algorithm>#include<stdio.h>#include<vector>#include<iomanip>using namespace std;int n, m;int single[2005];int doubl[2005];int dp[2005];int main(){ int t; scanf("%d", &t); while (t--){ scanf("%d", &m); for (int i = 1; i <= m; i++){ scanf("%d", &single[i]); } for (int i = 1; i <= m - 1; i++){ scanf("%d", &doubl[i]); } memset(dp, 0x3f, sizeof(dp)); dp[0] = 0; dp[1] = single[1]; for (int i = 2; i <= m; i++){ dp[i] = min(dp[i - 1] + single[i], dp[i - 2] + doubl[i - 1]); } int ans = dp[m]; int h = 0, min = 0, sec = 0; sec = ans % 60, ans /= 60; min = ans % 60; ans /= 60; h = (ans + 8)%24; bool mor = true; if (h > 12){ h -= 12; mor = false; } cout << setw(2) << setfill('0') << h << ':' << setw(2) << setfill('0') << min <<':' << setw(2) << setfill('0') << sec << ' ' << (mor ? "am" : "pm") << endl; } return 0;}

I - 最少拦截系统

最少不上升序列分割数等于最长上升序列的长度。(不会证= =)

#include<iostream>#include<string.h>#include<algorithm>#include<stdio.h>#include<vector>#include<iomanip>using namespace std;int n;int data[1000005];int a[1000005];int main(){ while (~scanf("%d", &n)){ if (n == 0){ printf("0/n"); continue; } for (int i = 0; i < n; i++){ scanf("%d", &data[i]); } int ans = 0; a[0] = data[0]; for (int i = 1; i < n; i++){ if (data[i]>a[ans]){ a[++ans] = data[i]; continue; } else{ for (int j = 0; j <= ans; j++){ if (data[i] <= a[j]){ a[j] = data[i]; break; } } } } printf("%d/n", ans+1); } return 0;}

J - FatMouse’s Speed

要保存路径,所以用pre数组保存前一个,

#include<iostream>#include<string.h>#include<algorithm>#include<stdio.h>//adawdawdusing namespace std;int n;struct pp{ int l, r,num;}p[1005];int dp[1005];int pre[1005];bool cmp(pp a, pp b){ if (a.l == b.l){ return a.r > b.r; } else return a.l < b.l;}int main(){ n = 1; while (~scanf("%d%d",&p[n].l,&p[n].r)){ dp[n] = 1; p[n++].num = n; } sort(p+1, p + n, cmp); int maxlen = 0, maxid = 0; //dp[1] = 1; for (int i = 1; i < n; i++){ for (int j = 1; j <i; j++){ if (p[i].l > p[j].l&&p[i].r<p[j].r&&dp[j] + 1>dp[i]){ dp[i] = dp[j] + 1; pre[i] = j; } } } for (int i = 1; i < n; i++){ if (dp[i] > maxlen){ maxlen = dp[i]; maxid = i; } } for (int i = maxlen; i >= 1; i--){ dp[i] = p[maxid].num; maxid = pre[maxid]; } printf("%d/n", maxlen); for (int i = 1; i <= maxlen; i++){ printf("%d",dp[i]); if (i < maxlen){ printf("/n"); } } return 0;}

K - Jury Compromise

难度挺大的,自己写的一直wa。dp[a][b]为选择a个人之后总控方分数-总辩方分数为b时控辩分和的最大值。 同时用path数组保存路径。

L - Common Subsequence

经典题目了, if (a[i] == b[j]){dp[i + 1][j + 1] = max(dp[i + 1][j + 1], dp[i][j] + 1);} else{dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1]);}

#include<iostream>#include<string.h>#include<algorithm>#include<stdio.h>#include<string>using namespace std;string a, b;int dp[1005][1005];//dp[a][b]字符串A的前a位和B的前b位最大公共长度int main(){ while (cin >> a >> b){ int lena = a.length(); int lenb = b.length(); memset(dp, 0, sizeof(dp)); for (int i = 0; i < lena; i++){ for (int j = 0; j < lenb; j++){ if (a[i] == b[j]){ dp[i + 1][j + 1] = max(dp[i + 1][j + 1], dp[i][j] + 1); } else{ dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1]); } } } printf("%d/n", dp[lena][lenb]); } return 0;}

M - Help Jimmy

这题不大懂得用dp解,看人别人的题解用的递归搜索,dfs(x,y,z)为从z桥的x坐标(x,y)处下落到下一块木板的用时。

#include<stdio.h>#include<string.h>#include<algorithm>#include<iostream>using namespace std;//dawdawdawint n, bx, by, maxd;const int inf = 10000000;int l[1005];int r[1005];struct ss{ int l, r, h;}s[1005];bool cmp(ss a, ss b){ return a.h > b.h; }int dfs(int x, int y, int cnt){//计算从当前板边缘下落到下一块板然后走到左右边缘要再次下落的过程 int nid=0; for (int i = cnt + 1; i <= n; i++){ if (s[i].l <= x&&s[i].r >= x&&s[i].h < y){ if (y - s[i].h>maxd){ return inf; } else{ nid = i; break; } } } if (nid == 0){ return (y>maxd?inf:y); } if (l[nid] == -1){ l[nid] = dfs(s[nid].l, s[nid].h, nid); r[nid] = dfs(s[nid].r, s[nid].h, nid); } int time = 0; time = min(l[nid] + x - s[nid].l, r[nid] + s[nid].r - x); time += y - s[nid].h; return time;}int main(){ int t; scanf("%d", &t); while (t--){ scanf("%d%d%d%d", &n, &bx, &by, &maxd); memset(l, -1, sizeof(l)); memset(r, -1, sizeof(r)); for (int i = 1; i <= n; i++){ scanf("%d%d%d", &s[i].l, &s[i].r, &s[i].h); } sort(s + 1, s + n + 1, cmp); printf("%d/n", dfs(bx, by, 0)); } return 0;}

N - Longest Ordered Subsequence

最长上升子序列长度,用前面的导弹防御题的代码就能ac。

O - Treats for the Cows

每天只能卖序列头或者尾的东西,价格为该物品的价格乘上天数。 dp[m][k]为第m天卖剩下开头为第k个的长度位n-m的序列所能获得的最大收入, dp[i][j] = max(dp[i - 1][j - 1] + s[j - 1] * i, dp[i - 1][j] + s[n - i + j]*i);

#include<iostream>#include<string.h>#include<algorithm>#include<stdio.h>#include<vector>#include<iomanip>using namespace std;int n;int s[2005];int dp[2005][2005];//dp[m][k]第m天剩下开头为第k个的长度位n-mint main(){ scanf("%d", &n); for (int i = 1; i <= n; i++)scanf("%d", &s[i]); for (int i = 1; i <= n; i++){ for (int j = 1; j <= i + 1; j++){ dp[i][j] = max(dp[i - 1][j - 1] + s[j - 1] * i, dp[i - 1][j] + s[n - i + j]*i); } } int ans = 0; for (int i = 1; i <= n; i++){ ans = max(ans, dp[n][i]); } printf("%d", ans); return 0;}

P - FatMouse and Cheese

也是看了别人的题解,用的dfs。

Q - Phalanx

求最大对称子矩阵,dp[i][j]为以第i行j列为子矩阵的左下角,所能得到的最大对称子矩阵的边长,所以枚举ij,求出其上方和右方的最大相同长度,然后与dp[i-1][j+1]比较来维护dp[i][j]

#include<iostream>#include<stdio.h>using namespace std;int n;char m[1005][1005];int dp[1005][1005];int main(){ while (~scanf("%d", &n), n){ int ans = 1; for (int i = 0; i < n; i++){ scanf("%s", m[i]); for (int j = 0; j < n; j++){ dp[i][j] = 1; } } for (int i = 1; i < n; i++){ for (int j = 0; j < n-1; j++){ int k = 1; while (j + k < n&&i - k >= 0 && m[i][j + k] == m[i - k][j]){ k++; } if (k >= dp[i - 1][j + 1] + 1)dp[i][j] = dp[i - 1][j + 1] + 1; else{ dp[i][j] = k; } ans = max(ans, dp[i][j]); } } printf("%d/n", ans); } return 0;}

R - Milking Time

挤牛奶,有开始时间结束时间和效率,每次工作完还要休息。求给定时间能获得的最大值。 按结束时间排序,然后顺序维护一边dp就行。 细节注意比较简单。

#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;int n,m,r;struct ss{ int beg, end, val;}s[1005];int dp[1000005];bool cmp(ss a, ss b){ return a.end < b.end;}int main(){ scanf("%d%d%d", &n, &m, &r); n += r; for (int i = 0; i < m; i++){ scanf("%d%d%d", &s[i].beg, &s[i].end, &s[i].val); s[i].beg += r; s[i].end += r; } sort(s, s + m, cmp); int maxval = 0; int time = 0; for (int i = 0; i <= m; i++){ for (; time < s[i].end; time++){ dp[time] = maxval; } if (time>n)break; maxval = max(maxval, dp[s[i].beg - r] + s[i].val); } printf("%d", maxval); return 0;}

S - Making the Grade

看了题解的,数据较水,给定的数列,问消耗最少是其变成非严格单调的,代码只得出了非严格递增的最小消耗。使用了离散化,因为数据有些值过大不能开出这么大的数组,而且非严格单调只需要将某个数字改成相邻数字一样便能得到最优解。还用了二维滚动数组减少空间消耗。 dp[i][j]为前i个维护成非严格递增时末尾为b[j]时消耗的最小值。

#include<stdio.h>#include<algorithm>#include<iostream>#include<cmath>using namespace std;const long long inf = 0x3f3f3f3f3f;int n;long long a[2005],b[2005];long long dp[2][2005];//二维滚动数组int main(){ while (~scanf("%d", &n)){ for (int i = 0; i < n; i++){ scanf("%d", &a[i]); b[i] = a[i]; } sort(b, b + n); //memset(dp, 0, sizeof(dp)); for (int i = 0; i < n; i++){ dp[0][i] = labs(a[0] - b[i]); } for (int i = 1; i < n; i++){ long long cnt = inf; for (int j = 0; j < n; j++){ cnt = min(cnt, dp[(i + 1) % 2][j]); dp[i % 2][j] = cnt+labs(a[i]-b[j]); } } long long ans = inf; for (int i = 0; i < n; i++){ ans = min(ans, dp[(n + 1) % 2][i]); } printf("%d/n", ans); } return 0;}
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表