[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)

HDU - 1074

解析:

先大致说说状态压缩,假设有三门作业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;
}

5.Super Jumping! Jumping! Jumping!

HDU - 1087

解析:注意题目是严格上升子序列并不是连续上升子序列(导致我写错了转移方程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

HDU - 1114

每种硬币的数量都不限制,因此是个完全背包。初始化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)

HDU - 1176

思路:

如果把时间轴看成行数,每个点在这个时间上获得的饼数看成列数,那么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

HDU - 1260

思路:

  1. 只能两个人一起买票,那么递推式很容易推出来:
  2. dp[i]=min(dp[i-1]+a[i] , dp[j-2]+b[i])
  3. 此处为单独买,和前一个人的买的较大值
  4. 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

HDU - 1160

#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

POJ - 1015

11.FatMouse and Cheese

HDU - 1078

思路:

  1. dp[i]日常表示前i个数的最大价值。
  2. 这题主要是把数据处理一下,把结束时间加上r,然后记得把结构体排序。
  3. 那么就转化成一维的最大子序列和了。
  4. 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

HDU - 2859

思路:

  1. 最大对称子矩阵,左下角到右上角为对称轴。
  2. 枚举每一个点,如果相同,把行数x向上扩展,列数y相右扩展。
  3. \(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;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值