【kuangbin】专题十二 基础DP1
https://vjudge.net/contest/68966#overview
前几周写了来着,忘更了
我饿了,先放代码,吃完再来
A - Max Sum Plus Plus
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int a[N], f[N]; //f[i-1]:max(dp[i-1][k])
int n, m, ans;
int main () {
while (~scanf ("%d%d", &m, &n)) {
memset (f, 0, sizeof f);
for (int i = 1; i <= n; i ++) cin >> a[i], a[i] += a[i-1];
for (int i = 1; i <= m; i ++) {
int tmp = a[i]; //dp[i-1][j]
//for (int j = 1; j <= i; j ++) tmp += a[j];
ans = tmp; //前i项成i段
for (int j = i + 1; j <= n; j ++) {
tmp = max (tmp, f[j-1]) + a[j] - a[j-1];
//dp[i][j] = max (dp[i][j-1]+a[j], max(dp[i-1][k])+a[j]);
f[j-1] = ans;
ans = max (ans, tmp);
}
}
cout << ans << endl;
}
}
//m个最大字段和
//朴素的dp式子:
//dp[i][j]: 以a[j]结尾的,i个段的最大和
//dp[i][j] = max (dp[i][j-1]+a[j], max(dp[i-1][k])+a[j]);
//其中k表示在[i-1, j-1]范围内最大的dp[i-1][k]值
//优化:
//把max(dp[i-1][k])存起来
B - Ignatius and the Princess IV
#include <bits/stdc++.h>
using namespace std;
const int N = 1e7 + 5;
int n, a[N];
int main () {
while (cin >> n) {
for (int i = 0; i < n; i ++) {
cin >> a[i];
}
sort (a, a + n);
cout << a[n/2] << endl;
}
}
C - Monkey and Banana
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int n, f[N];
struct Node {
int a, b, c;
void set (int x, int y, int z) {
if (x > y) swap (x, y);
a = x, b = y, c = z;
}
bool operator < (const Node &t) const {
if (a != t.a) return a < t.a;
if (b != t.b) return b < t.b;
return c < t.c;
}
}e[N];
void solve () {
for (int i = 1; i <= n; i ++) {
int x, y, z;
cin >> x >> y >> z;
e[i].set(x, y, z);
e[i+n].set(y, z, x);
e[i+n*2].set(z, x, y);
}
sort (e + 1, e + 3*n + 1);
f[1] = e[1].c;
int ans = 0;
for (int i = 1; i <= 3*n; i ++) {
int maxn = 0;
for (int j = 1; j < i; j ++) { //找出第i个之前的最大高度
if (e[j].a < e[i].a && e[j].b < e[i].b) {
maxn = max (maxn, f[j]);
}
}
f[i] = e[i].c + maxn;
ans = max (ans, f[i]);
}
cout << ans << endl;
}
int main () {
int t = 1;
while (cin >> n, n) {
cout << "Case " << t << ": maximum height = ";
solve ();
t ++;
}
}
//上一块选定为i了,那么下一块一定是叠在i的最佳高度上。
D - Doing Homework
#include <bits/stdc++.h>
using namespace std;
const int N = 20, M = (1<<15) + 5;
int n;
int f[M], tt[M], ans[M]; //扣分最小值,完成时间,最优时以何门作业最后完成
struct Node {
string s;
int ddl, dx;
}e[N];
void print_name (int x) {
if (x == 0) return ;
print_name (x - (1 << (ans[x]-1)));
cout << e[ans[x]].s << endl;
}
void solve () {
cin >> n;
int m = (1<<n) - 1;
for (int i = 1; i <= n; i ++) {
string s;
int ddl, dx;
cin >> s >> ddl >> dx;
e[i] = {s, ddl, dx};
}
for (int i = 1; i <= m; i ++) {
f[i] = 0x3f3f3f3f;
for (int j = n; j >= 1; j --) { //倒叙保证字典序最小
int st = (1<<(j-1)); //第 j 门在转态 i 的对应位置
if ((i & st) == 0) continue; //转态 i 中没有完成作业 j
int cost = max (tt[i-st] + e[j].dx - e[j].ddl, 0); //以第 j 门作业最后完成时的将要扣的分
if (f[i] > f[i-st] + cost) {
f[i] = f[i-st] + cost;
tt[i] = tt[i-st] + e[j].dx;
ans[i] = j; ////转态 i 扣分最少时,是以何门作业最后完成
}
}
}
cout << f[m] << endl;
print_name(m);
}
int main () {
int t;
cin >> t;
while (t --) {
solve ();
}
}
//subject ddl 耗时
//按ddl排序,靠前的排在前面,然后
//用dp[i] , 0 <= i<(1<<N) 代表每个状态的扣分的最小值
//对于每个状态i,枚举其每个已完成的作业j,
//然后求出以每个作业j为最后一门完成时的最小值
E - Super Jumping! Jumping! Jumping!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1005;
int a[N], n;
ll f[N];
void solve () {
memset (f, 0, sizeof f);
ll ans = 0;
for (int i = 1; i <= n; i ++) cin >> a[i], f[i] = a[i];
for (int i = 1; i <= n; i ++) {
for (int j = i + 1; j <= n; j ++) {
if (a[j] > a[i])
f[j] = max (f[j], f[i] + a[j]);
}
ans = max (ans, f[i]);
}
cout << ans << endl;
}
int main () {
while (cin >> n, n) {
solve ();
}
}
//最大上升子序列之和
F - Piggy-Bank
#include <bits/stdc++.h>
using namespace std;
const int N = 505, M = 10005;
int st, ed, n, m;
int v[N], w[N], f[M];
void solve () {
memset (f, 0x3f, sizeof f);
cin >> st >> ed >> n;
m = ed - st;
for (int i = 0; i < n; i ++)
cin >> w[i] >> v[i];
f[0] = 0;
for (int i = 0; i < n; i ++) {
for (int j = v[i]; j <= m; j ++) {
f[j] = min (f[j], f[j-v[i]] + w[i]);
}
}
if (f[m] == 0x3f3f3f3f) cout << "This is impossible.\n";
else cout << "The minimum amount of money in the piggy-bank is " << f[m] << "." << endl;
}
int main () {
int t;
cin >> t;
while (t --) {
solve ();
}
}
//容量为m,完全背包
//能装满m的最小价值
//一定要放满
G - 免费馅饼
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
typedef pair<int, int> pii;
int n;
int f[N][15]; //t,x
int dx[] = {-1, 0, 1};
void solve () {
memset (f, 0, sizeof f);
int maxn = 0;
for (int i = 0; i < n; i ++) {
int x, t;
scanf ("%d%d", &x, &t);
f[t][x] ++;
maxn = max (maxn, t);
}
//倒推
for (int t = maxn-1; t >= 0; t --) {
f[t][0] += max (f[t+1][0], f[t+1][1]);
for (int x = 1; x < 10; x ++) {
f[t][x] += max (f[t+1][x], max (f[t+1][x+1], f[t+1][x-1]));
}
f[t][10] += max (f[t+1][10], f[t+1][9]);
}
cout << f[0][5] << endl;
}
int main () {
while (scanf ("%d", &n), n) {
solve ();
}
}
//0-10这11个位置
//初始在5
H - Tickets
#include<bits/stdc++.h>
using namespace std;
const int N = 2005;
int a[N], b[N], f[N], n;
void solve () {
memset (f, 0x3f, sizeof f);
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i < n; i ++) cin >> b[i];
f[0] = 0, f[1] = a[1];
for (int i = 2; i <= n; i ++) {
f[i] = min (f[i-1] + a[i], f[i-2] + b[i-1]);
}
int ans = f[n];
int h = ans / 3600, m = (ans-h*3600)/60, s = ans % 60;
h += 8;
printf("%02d:%02d:%02d ",h,m,s);
if (h > 12) cout << "pm\n";
else cout << "am\n";
}
int main () {
int t;
cin >> t;
while (t --) {
solve ();
}
}
//要么一个一个拿,要么两个一起拿
I - 最少拦截系统
#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 5;
int n, a[N], f[N];
void solve () {
//vector <int> a(n+1), f(n+1, 1);
for (int i = 1; i <= n; i ++) cin >> a[i];
int ans = 1;
f[1] = a[1];
for (int i = 1; i <= n; i ++) {
if (a[i] > f[ans]) f[++ans] = a[i];
else {
int pos = lower_bound (f+1, f+ans+1, a[i]) - f;
f[pos] = a[i];
}
}
cout << ans << endl;
}
int main () {
while (cin >> n)
solve ();
}
//最长上升子序列
J - FatMouse's Speed
#include <bits/stdc++.h>
using namespace std;
const int N = 10005;
int f[N], ans[N], n;
struct Node {
int id, w, s;
bool operator <(const Node &t) const {
return w > t.w;
}
}e[N];
int main () {
int w, s;
while (cin >> w >> s) {
e[++ n] = {n, w, s};
}
sort (e + 1, e + n + 1);
int len = 0, k = 1;
f[1] = e[1].s, ans[1] = 1;
for (int i = 1; i <= n; i ++) {
if (f[k] < e[i].s) {
f[++k] = e[i].s;
ans[i] = k;
}
else {
int pos = lower_bound (f, f+k+1, e[i].s) - f;
f[pos] = e[i].s, ans[i] = pos;
}
}
cout << k << endl;
for (int i = n; i >= 1; i --) {
if (k == ans[i]) {
k --;
cout << e[i].id << endl;
}
}
}
//重量增大时速度变小,问最多有多少只这样的老鼠
//根据重量排序然后找速度的:
//最长上升子序列 + 记录路径
K - Jury Compromise
$f[i][j][k]: $ 前 \(i\) 个人中选 \(j\) 个人,差值为 \(k=p_i-d_i\) 的方案中,\(p_i+d_i\) 最大的方案。考虑到 \(k\) 可能为负数,所以要加一个偏移量\(base\),把原范围 \([-400,400]\) 变为 \([0,800]\)。
关键步骤:状态转移 -> 找最小差值 -> 输出路径
转移
不选:\(f[i][j][k]=f[i-1][j][k]\)
选:\(f[i][j][k]=f[i-1][j-1][k-(p_i-d_i)]+d_i+p_i\)
选的话必须先满足:\(j>0\) 且 \(k\in [0,800]\)
找最小差值
最小差值 \(v\) 从0开始往上找,找到第一个满足 \(f[n][m][v+base] \geq 0\) 或 \(f[n][m][base-v] \geq 0\) 的 \(v\),注意二者都符合的话就找\(f\) 值更大的
记录路径
倒着推回去,如果 \(f\) 值不变,代表没入选,否则记录答案。
记得输出的时候再排下序
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#define endl "\n"
using namespace std;
const int N = 205, M = 25, base = 400;
int n, m;
int p[N], d[N], f[N][M][805], ans[N];
void solve () {
memset (f, -0x3f, sizeof f);
for (int i = 1; i <= n; i++) cin >> p[i] >> d[i];
//dp
f[0][0][base] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) {
for (int k = 0; k <= 800; k++) {
f[i][j][k] = f[i-1][j][k];
if (j == 0) continue;
int dx = k - (p[i] - d[i]);
if (dx >= 0 && dx <= 800) {
f[i][j][k] = max (f[i][j][k], f[i-1][j-1][dx] + p[i] + d[i]);
}
}
}
}
//找最小差值
int v = 0;
while (f[n][m][v+base] < 0 && f[n][m][base-v] < 0) v ++;
if (f[n][m][v+base] >= f[n][m][base-v]) v = v + base;
else v = base - v;
//找答案
int k = 0;
for (int i = n, j = m; i >= 1 && j >= 0; i--) {
if (f[i][j][v] == f[i-1][j][v]) continue;
ans[++ k] = i;
v -= (p[i] - d[i]);
j --;
}
int P = 0, D = 0;
for (int i = 1; i <= k; i++) {
P += p[ans[i]], D += d[ans[i]];
}
cout << "Best jury has value " << P << " for prosecution and value " << D << " for defence:\n";
sort (ans + 1, ans + k + 1);
for (int i = 1; i <= k; i++) cout << ' ' << ans[i];
cout << endl << endl;
}
int main () {
ios::sync_with_stdio (0);cin.tie(0);cout.tie(0);
int t = 1;
while (cin >> n >> m, n || m) {
cout << "Jury #" << t ++ << endl;
solve ();
}
}
L - Common Subsequence
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1005;
string a, b;
int n, m;
int f[N][N];
int main () {
while (cin >> a >> b) {
memset (f, 0, sizeof f);
n = a.size(), m = b.size();
for (int i = 0; i < n; i ++)
for (int j = 0; j < m; j ++) {
if (a[i] == b[j]) f[i+1][j+1] = f[i][j] + 1;
else f[i+1][j+1] = max (f[i][j+1], f[i+1][j]);
}
cout << f[n][m] << endl;
}
}
//最长公共子序列长度
//f[i][j]: f[i][j]=max(f[i-1][j], f[i][j-1]);
//if(a[i] == b[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1);
M - Help Jimmy
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 1005, inf = 2e6;
int f[N][2]; // left/right
int n, x, y, maxh;
struct Node {
int l, r, h;
bool operator<(const Node &t) const {
return h < t.h;
}
}e[N];
void Left (int cur) { //求第m层向左走到地面的最少时间
int nxt = cur - 1; //下一层
while (nxt && e[cur].h - e[nxt].h <= maxh) { //没到地面且还能往下
if (e[cur].l >= e[nxt].l && e[cur].l <= e[nxt].r) {
int dx = min (e[cur].l - e[nxt].l + f[nxt][0], e[nxt].r - e[cur].l + f[nxt][1]);
f[cur][0] = e[cur].h - e[nxt].h + dx;
return ;
}
else nxt --;
}
if (e[cur].h - e[nxt].h > maxh) f[cur][0] = inf;
else f[cur][0] = e[cur].h; //可以跳到地面
}
void Right (int cur) { //求第m层向右走到地面的最少时间
int nxt = cur - 1; //下一层
while (nxt && e[cur].h - e[nxt].h <= maxh) { //没到地面且还能往下
if (e[cur].r <= e[nxt].r && e[cur].r >= e[nxt].l) {
int dx = min (e[cur].r - e[nxt].l + f[nxt][0], e[nxt].r - e[cur].r + f[nxt][1]);
f[cur][1] = e[cur].h - e[nxt].h + dx;
return ;
}
else nxt --;
}
if (e[cur].h - e[nxt].h > maxh) f[cur][1] = inf;
else f[cur][1] = e[cur].h; //可以跳到地面
}
void solve () {
//memset (f, 0x3f, sizeof f);
cin >> n >> x >> y >> maxh;
e[n+1].l=x, e[n+1].r=x, e[n+1].h=y;
e[0].l=-inf, e[0].r=inf, e[0].h=0;
// e[n+1] = {x, x, y}, e[0] = {-inf, inf, 0};
for (int i = 1; i <= n; i ++) {
int l, r, h;
cin >> e[i].l >> e[i].r >> e[i].h;
//e[i] = {l, r, h};
}
sort (e, e + n + 2);
for (int i = 1; i <= n+1; i ++) {
Left(i), Right (i);
}
cout << min (f[n+1][0], f[n+1][1]) << endl;
}
int main () {
int t;
cin >> t;
while (t --) {
solve ();
}
}
N - Longest Ordered Subsequence
#include <iostream>
using namespace std;
const int N = 1005;
int f[N], a[N], n, ans;
int main () {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i <= n; i ++) {
f[i] = 1;
for (int j = 1; j < i; j ++) {
if (a[i] > a[j])
f[i] = max (f[i], f[j] + 1);
}
ans = max (f[i], ans);
}
cout << ans << endl;
}
//最长上升子序列长度
O - Treats for the Cows
#include <iostream>
#include <cmath>
using namespace std;
const int N = 2e3 + 5;
int f[N][N], a[N], n;
int main () {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i <= n; i ++) f[i][n+1] = f[i-1][n+1] + i * a[i];
for (int i = n; i >= 1; i --) f[0][i] = f[0][i+1] + (n - i + 1) * a[i];
for (int i = 1; i <= n; i ++)
for (int j = n; j > i; j --) {
int k = i + (n - j + 1); //总共选了多少
f[i][j] = max (f[i-1][j] + k * a[i], f[i][j+1] + k * a[j]);
}
int ans = 0;
for (int i = 0; i <= n; i ++) ans = max (ans, f[i][i+1]);
cout << ans << endl;
}
//两端取
//dp[i][j]:前面取了a[0~i],后面取了a[j~n+1]的最大代价
P - FatMouse and Cheese
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int a[N][N], f[N][N], n, k;
int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
bool Range (int x, int y) {
if (x < 1 || x > n || y < 1 || y > n)
return false;
return true;
}
int dfs (int x, int y) {
if (f[x][y]) return f[x][y];
int maxn = a[x][y]; //至少能走自己这一个
for (int i = 1; i <= k; i ++)
for (int j = 0; j < 4; j ++) {
int xx = x + dx[j] * i, yy = y + dy[j] * i;
if (!Range(xx, yy) || a[xx][yy] <= a[x][y]) continue;
maxn = max (maxn, dfs (xx, yy) + a[x][y]);
}
f[x][y] = maxn;
return f[x][y];
}
void solve () {
memset (f, 0, sizeof f);
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= n; j ++)
cin >> a[i][j];
dfs (1, 1);
cout << f[1][1] << endl;
}
int main () {
while (cin >> n >> k, (n+1)&&(k+1)) {
solve ();
}
}
//每个格子有一个数a[i][j](范围在0~100)。每次只能横或竖直跳,且最多跳k格,跳的下一位数要比当前位大
//从(1,1)开始跳,问跳的数的和的最大值
//经典滑雪题
Q - Phalanx
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
char a[N][N];
int f[N][N], n, ans;
void solve () {
memset (f, 0, sizeof f);
ans = 0;
//cin >> n;
for (int i = 0; i < n; i ++) cin >> a[i];
for (int i = 0; i < n; i ++)
for (int j = 0; j < n; j ++) {
f[i][j] = 1;
}
for (int i = 0; i < n; i ++) {
for (int j = n-1; j >= 0; j --) {
int ii = i, jj = j;
ii --, jj ++;
while (ii >= 0 && jj < n && f[i][j] <= f[i-1][j+1] && a[ii][j] == a[i][jj]) {
ii --, jj ++;
f[i][j] ++;
}
ans = max (ans, f[i][j]);
}
}
cout << ans << endl;
}
int main () {
while (cin >> n, n) {
solve ();
}
}
//f[x][y]表示以(x,y)为左下角点的矩阵。
//对称矩阵f[i][d]是在对称矩阵f[i-1][d+1] 的基础上做出的
R - Milking Time
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1005;
int n, m, R;
int f[N];
struct Node {
int l, r, w;
bool operator <(const Node &t) const {
return l < t.l;
}
}e[N];
int main () {
cin >> n >> m >> R;
for (int i = 0; i < m; i ++) {
cin >> e[i].l >> e[i].r >> e[i].w;
e[i].r += R; //+休息时间
}
sort (e, e + m);
int ans = 0;
for (int i = 0; i < m; i ++) {
f[i] = e[i].w;
for (int j = 0; j < i; j ++) {
if (e[j].r > e[i].l) continue;
f[i] = max (f[i], f[j] + e[i].w);
}
ans = max (ans, f[i]);
}
cout << ans << endl;
}
S - Making the Grade
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2005;
int a[N], b[N], f[2][N];
int n;
int main () {
cin >> n;
for (int i = 0; i < n; i ++) cin >> a[i], b[i] = a[i];
sort (b, b + n);
for (int i = 0; i < n; i ++) f[0][i] = abs (a[0]-b[i]);
for (int i = 1; i < n; i ++) {
int lst = f[(i-1)&1][0];
for (int j = 0; j < n; j ++) {
lst = min (lst, f[(i-1)&1][j]);
f[i&1][j] = abs (a[i] - b[j]) + lst;
}
}
int ans = 1e9;
for (int i = 0; i < n; i ++) ans = min (ans, f[(n-1)&1][i]);
cout << ans << endl;
}
//给定一个序列,以最小代价将其变为单调不增或单调不减序列
//dp[i][j]:当前处理到第i个数,j表示在b序列中排第j的数
//离散化 + dp + 滚动数组优化
dp 太太太太太太太太太难了
标签:专题,const,DP1,int,kuangbin,++,ans,using,include From: https://www.cnblogs.com/CTing/p/16545816.html