[kuangbin带你飞]专题十二 基础DP1 题解+总结
kuangbin带你飞:点击进入新世界
文章目录
目录
1.Max Sum Plus Plus
原题链接:传送门
2.Ignatius and the Princess IV
原题链接:传送门
思路:hash存储(感觉和dp没啥关系啊。。)
#include<bits/stdc++.h>
using namespace std;
map<int, int>mp; int n, t;
int main() {
freopen("in.txt", "r", stdin);
ios::sync_with_stdio(false); cin.tie(0);
while (cin >> n) {
mp.clear();
for (int i = 0; i < n; ++i) { cin >> t; mp[t]++; }
for (auto &p : mp) {
if (p.second >= (n + 1) / 2) { cout << p.first << endl; break; }
}
}
}
3.Monkey and Banana
原题链接:传送门
解析:对于所给的砖块可以有6种组合(即:长宽高打乱)所以最终的 $ index = 6 * n$
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000;
struct node {
int l, r, w;//长宽高
}a[maxn];
int n, r, w, l;
bool cmp(node &a, node &b) {
if (a.l == b.l)return a.r < b.r;
return a.l < b.l;
}
int dp[maxn];
int main() {
freopen("in.txt", "r", stdin);
ios::sync_with_stdio(false); cin.tie(0);
int Case = 1;
while (cin >> n && n) {
int index = 1;
for (int i = 0; i < n; ++i) {
cin >> l >> r >> w;
a[index].l = l, a[index].r = r, a[index++].w = w;
a[index].l = r, a[index].r = l, a[index++].w = w;
a[index].l = w, a[index].r = r, a[index++].w = l;
a[index].l = l, a[index].r = w, a[index++].w = r;
a[index].l = r, a[index].r = w, a[index++].w = l;
a[index].l = w, a[index].r = l, a[index++].w = r;
}
sort(a + 1, a + index + 1,cmp);//根据长宽排序
memset(dp, 0,sizeof dp);
int ans = 0;
for(int i = 1;i <= index;++i)
for (int j = 1; j <= index; ++j) {
if (a[i].r < a[j].r && a[i].l < a[j].l)
dp[j] = max(dp[j], dp[i] + a[j].w), ans = max(ans, dp[j]);
}
cout << "Case " << Case++ << ": maximum height = " << ans << endl;
}
}
4.Doing Homework (状态压缩DP)
解析:
先大致说说状态压缩,假设有三门作业a,b,c
那么,abc都做完即111,111可由101,110,011任意一个来得到。而101可以从100或者001来得到,这就是状态压缩dp的一个基本的状态转移。
#include<bits/stdc++.h>
using namespace std;
const int N = 16;
struct Node
{
char str[109];
int want, need;
}node[N];
struct DP
{
int now, sum, next, pos;
}dp[1 << N];
void put_ans(int x)
{
if (dp[x].next != -1)
{
put_ans(dp[x].next);
printf("%s\n", node[dp[x].pos].str);
}
}
int main()
{
freopen("in.txt", "r", stdin);
int T;
scanf("%d", &T);
while (T--)
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%s%d%d", node[i].str, &node[i].want, &node[i].need);
dp[0].now = dp[0].sum = 0;
dp[0].next = dp[0].pos = -1;
int m = (1 << n) - 1;
for (int i = 1; i <= m; i++)
{
dp[i].sum = 0x3f3f3f3f;
for (int j = 0; j < n; j++)
{
if ((1 << j) & i)
{
int k = i - (1 << j);
int v = dp[k].now + node[j].need - node[j].want;
v = max(v, 0);
if (dp[i].sum >= dp[k].sum + v)
{
dp[i].sum = dp[k].sum + v;
dp[i].now = dp[k].now + node[j].need;
dp[i].next = k;
dp[i].pos = j;
}
}
}
}
printf("%d\n", dp[m].sum);
put_ans(m);
}
return 0;
}
// update 2022.2.28
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 16;
struct node {
string str;
int want, need;
} a[N];
struct DP {
int now, sum, next, pos;
} dp[1 << N];
void put_ans(int x) {
if (dp[x].next != -1) {
put_ans(dp[x].next);
cout << a[dp[x].pos].str << "\n";
}
}
int n, t;
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
cout << fixed << setprecision(20);
cin >> t;
while (t--) {
cin >> n;
for (int i = 0; i < n; i += 1) cin >> a[i].str >> a[i].want >> a[i].need;
dp[0].now = dp[0].sum = 0;
dp[0].next = dp[0].pos = -1;
int m = (1 << n) - 1;
for (int i = 1; i <= m; i += 1) {
dp[i].sum = 0x3f3f3f3f;
for (int j = 0; j < n; j += 1) {
if ( (1 << j) & i ) {
int k = i - (1 << j);
int v = dp[k].now + a[j].need - a[j].want;
v = max(v, 0);
if (dp[i].sum >= dp[k].sum + v) {
dp[i].sum = dp[k].sum + v;
dp[i].now = dp[k].now + a[j].need;
dp[i].next = k;
dp[i].pos = j;
}
}
}
}
cout << dp[m].sum << "\n";
put_ans(m);
}
}
5.Super Jumping! Jumping! Jumping!
解析:注意题目是严格上升子序列并不是连续上升子序列(导致我写错了转移方程2333)
#include<bits/stdc++.h>
using namespace std;
#define ms(a,b) (a,b,sizeof a)
const int maxn = 1e3 + 10;
int dp[maxn], a[maxn];
int n;
int main() {
freopen("in.txt", "r", stdin);
ios::sync_with_stdio(false); cin.tie(0);
while (cin >> n && n) {
ms(dp, 0);
for (int i = 0; i < n; ++i)
cin >> a[i], dp[i] = a[i];
int ans = 0;
for (int i = 0; i < n; ++i)
{
for (int j = i + 1; j < n; ++j)
{
if (a[j] > a[i])
dp[j] = max(dp[j], dp[i] + a[j]);
}
ans = max(ans, dp[i]);
}
cout << ans << endl;
}
}
同样是LIS模板题:最少拦截系统:传送门
6.Piggy-Bank
每种硬币的数量都不限制,因此是个完全背包。初始化dp数组时需注意,因为求的最小,且题目的意思是恰好装满,所以dp数组初始化为INF,但dp[0] = 0,意为此时只有容量为0 的背包可以在什么也不装且价值为0 的情况下被“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态(背包九讲)
#include<bits/stdc++.h>
using namespace std;
const int N = 10010;
const int inf = 0x3f3f3f3f;
int dp[N], v[N], w[N];
int n, t, e, f;
int main() {
//freopen("in.txt","r",stdin);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> t; while (t--) {
cin >> e >> f;
int W = f - e;
cin >> n;
for (int i = 1; i <= n; ++i)cin >> v[i] >> w[i];
memset(dp, 0x3f, sizeof dp);
dp[0] = 0;
for (int i = 1; i <= n; ++i)
for (int j = w[i]; j <= W; ++j)
dp[j] = min(dp[j], dp[j - w[i]] + v[i]);
if(dp[W] == inf )printf("This is impossible.\n");
else printf("The minimum amount of money in the piggy-bank is %d.\n", dp[W]);
}
}
7.免费馅饼 (数塔 + 逆向DP)
思路:
如果把时间轴看成行数,每个点在这个时间上获得的饼数看成列数,那么a[i][j]表示在时间为i时j点获得的饼,如果把图画出来就可以发现这其实是一道数塔题。
因为要处理相邻两边,下标为0的时候不好处理,所以把位置+1;
既然是数塔题,那么显而易见:
$ dp[i][j] = max({dp[i + 1][j - 1],dp[i + 1][j], dp[i + 1][j + 1]}) + a[i][j];$
这道题再升级一下就是加上高度的。POJ 1661Help Jimmy(逆向DP Or 记忆化搜索 Or 最短路径)
#include<bits/stdc++.h>
using namespace std;
#define ms(a,b) memset(a,b,sizeof a);
const int maxn = 1e5 + 50;
int t, x, y;
int a[maxn][15], dp[maxn][15];
int main() {
//freopen("in.txt","r",stdin);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
while (cin >> t && t) {
ms(dp, 0);
ms(a, 0);
int e = 0;
for (int i = 0; i < t; ++i) { cin >> x >> y; a[y][++x]++; e = max(e, y); }
for (int i = e; i >= 0; i--)
for (int j = 1; j <= 11; j++)
dp[i][j] = max({dp[i + 1][j - 1],dp[i + 1][j], dp[i + 1][j + 1]}) + a[i][j];
cout << dp[0][6] << endl;
}
}
8.Tickets
思路:
- 只能两个人一起买票,那么递推式很容易推出来:
- dp[i]=min(dp[i-1]+a[i] , dp[j-2]+b[i])
- 此处为单独买,和前一个人的买的较大值
- dp[n]为答案 。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2010;
int a[maxn], b[maxn], dp[maxn];
int n, m, r, t = 0;
int main() {
//freopen("in.txt","r",stdin);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
while (cin>>t) {
while (t--) {
cin >> n;
for (int i = 1; i <= n; ++i)cin >> a[i];
for (int i = 2; i <= n; ++i)cin >> b[i];
dp[1] = a[1];
for (int i = 2; i <= n; ++i)dp[i] = min(dp[i - 1] + a[i], dp[i - 2] + b[i]);
int h = 8, mi = 0, sec = 0;
sec += dp[n];
mi += sec / 60 % 60;
h += sec / 3600;
sec %= 60;
printf("%02d:%02d:%02d ", h, mi, sec);
if (h >= 12) printf("pm\n");
else printf("am\n");
}
}
}
9.FatMouse's Speed
#include<bits/stdc++.h>
using namespace std;
struct Mouse
{
int id;
int speed;
int weight;
int pre;
int dp;
} mouse[10010];
int cmp(Mouse a, Mouse b)
{
if (a.weight == b.weight)
{
return a.speed > b.speed;
}
return a.weight < b.weight;
}
int dfs(int p)
{
//printf("@%d %d\n",p,mouse[p].pre);
if (p == -1) return 0;
dfs(mouse[p].pre);
printf("%d\n", mouse[p].id);
return 0;
}
int main()
{
int i = 0;
while (~scanf("%d%d", &mouse[i].weight, &mouse[i].speed))
{
mouse[i].id = i + 1;
mouse[i].pre = -1;
mouse[i].dp = 1;
i++;
}
sort(mouse, mouse + i, cmp);
int n = i;
mouse[0].dp = 1;
int ans = 1;
int p = 0;
for (int i = 1; i < n; i++)
{
for (int j = 0; j < i; j++)
{
if (mouse[i].weight > mouse[j].weight && mouse[i].speed < mouse[j].speed)
{
if (mouse[j].dp + 1 > mouse[i].dp)
{
mouse[i].dp = mouse[j].dp + 1;
mouse[i].pre = j;
}
}
}
if (mouse[i].dp > ans)
{
ans = mouse[i].dp;
p = i;
}
}
/*
for(int i=0;i<n;i++)
{
printf("id:%d w:%d s:%d pre:%d dp:%d\n",mouse[i].id,mouse[i].weight,mouse[i].speed,mouse[i].pre,mouse[i].dp);
}*/
printf("%d\n", ans);
dfs(p);
return 0;
}
10.Jury Compromise
11.FatMouse and Cheese
思路:
- dp[i]日常表示前i个数的最大价值。
- 这题主要是把数据处理一下,把结束时间加上r,然后记得把结构体排序。
- 那么就转化成一维的最大子序列和了。
- dp[i]=max(dp[i],dp[k]+a[j].w) when a[k].r<=a[i].l&&l>k
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
int dp[maxn][maxn], a[maxn][maxn];
int nx[] = { 0,1,0,-1 };
int ny[] = { 1,0,-1,0 };
int n, m, r, t = 1;
int dfs(int x, int y) {
if (dp[x][y]) return dp[x][y];
int mx = 0;
for (int i = 1; i <= m; ++i) {
for (int j = 0; j < 4; ++j) {
int xx = x + nx[j] * i, yy = y + ny[j] * i;
if (a[x][y] < a[xx][yy] && xx>0 && yy > 0 && xx <= n && yy <= n)
mx = max(dfs(xx, yy), mx);
}
}
return dp[x][y] = a[x][y] + mx;
}
int main() {
//freopen("in.txt","r",stdin);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
while (cin >> n >> m && n != -1 &&m != -1) {
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
cin >> a[i][j];
memset(dp, 0, sizeof dp);
cout << dfs(1, 1) << endl;
}
}
12.Phalanx
思路:
- 最大对称子矩阵,左下角到右上角为对称轴。
- 枚举每一个点,如果相同,把行数x向上扩展,列数y相右扩展。
- \(dp[i][j]=min(dp[i-1][j+1],i-x-1)+1 \quad when\quad a[i][j+1]==a[i-1][j]\)
#include<bits/stdc++.h>
using namespace std;
#define ms(a,b) memset(a,b,sizeof a);
const int N = 1010;
char a[N][N];
int dp[N][N];
int n;
int main() {
freopen("in.txt","r",stdin);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
while (cin >> n && n) {
ms(dp, 0);
int ans = 1;
for (int i = 1; i <= n; ++i)for (int j = 1; j <= n; ++j)cin >> a[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
dp[i][j] = 1;
int x = i, y = j;
while (a[i][y] == a[x][j])x--, y++;
if(a[i][j + 1] == a[i - 1][j])
dp[i][j] = min(dp[i - 1][j + 1], i - x - 1) + 1;
ans = max(ans, dp[i][j]);
}
cout << ans << endl;
}
}