「作业」线性DP

这个作业有点多,尽量一点点地每一道题都写得详细一点吧。


A. 数字三角形 Number Triangles#

传送门:水滴

题目大意:给你一个由正整数组成的三角形,让你选择一条路径,是这条路径的权值和最大。

首先拿到题考虑贪心,但是贪心的做法局限性很大,样例都过不了,否掉。

考虑 dp,发现逆向求解更优,于是观察样例:由每一行往上转移,每次取最大的,最后三角形顶就是答案。

点击查看代码
#include <bits/stdc++.h>
#define ll long long

using namespace std;

const int maxn = 1e4 + 10;

int r;
int f[maxn][maxn];

int main() {
	scanf("%d", &r);
	for (int i = 1; i <= r; i++) {
		for (int j = 1; j <= i; j++) {
			scanf("%d", &f[i][j]);
		}
	}
	for (int i = r - 1; i >= 1; i--) {
		for (int j = 1; j <= i; j++) {
			f[i][j] += max(f[i + 1][j + 1], f[i + 1][j]);
		}
	}
	return printf("%d", f[1][1]), 0;
}

B. 导弹拦截#

传送门:水滴

题目大意:给定一个数列,求它的最长下降子序列和最长上升子序列。

从第一问可知答案是最长上升子序列的长度。第二问其实就是让我们求上升子序列的最少个数。根据 Dilworth 定理可知,第二问其实就是让我们求最长下降子序列的长度。可以很快写出 O(n2) 代码。

点击查看代码
#include <bits/stdc++.h>
#define ll long long

using namespace std;

const int maxn = 1e5 + 50;

int n;
int a[maxn];
int f1[maxn], f2[maxn];
int ans = -1;
int res = -1;

int main()
{
	int x;
	while (cin >> x) a[++n] = x;

	f1[1] = 1;
	for (int i = 2; i <= n; i++) {
		f1[i] = 1;
		for (int j = 1; j < i; j++)
			if (a[j] >= a[i])
				f1[i] = max(f1[i], f1[j] + 1);
        ans = max(f1[i], ans);
	}
	printf("%d\n", ans);

	f2[1] = 1;
	for (int i = 2; i <= n; i++) {
		f2[i] = 1;
		for (int j = 1; j < i; j++)
			if (a[j] < a[i])
				f2[i] = max(f2[i], f2[j] + 1);
        res = max(f2[i], res);
	}
	printf("%d\n", res);
	return 0;
}

但是题目的数据范围是 105 的,所以 O(n2) 过不去。考虑优化。(呃先不考虑了)


C. 合唱队形#

传送门:水滴

题目大意:给出 n 个正整数,从中取出若干个数留下 k 个数,使整个数列从左到右从右到左递增到同一个数。求出最小的 nk

看完题不难想到分别做两次 dp。先从左到右做一次最长上升子序列,再从右到左做一次最长上升子序列。最后 O(n) 扫一遍数列,相当于枚举那个最中间、最高的那个人,用第一遍的数组和第二遍的数组的第 i 个加起来再减 1(因为 a[i] 出现了两次)就是剩下的人。因为答案要求 nk 所以还要用 n 减去那个值。

Ps.呃貌似是独立切黄哎(

点击查看代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int maxn = 1e2 + 50;

int n;
int t[maxn];
int f1[maxn], f2[maxn];

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
        scanf("%d", &t[i]);

    f1[1] = 1;
    for (int i = 2; i <= n; i++) {
        f1[i] = 1;
        for (int j = 1; j < i; j++)
            if (t[j] < t[i])
                f1[i] = max(f1[i], f1[j] + 1);
    }
    // cout << f1[4] << '\n';

    f2[n] = 1;
    for (int i = n; i >= 1; i--) {
        f2[i] = 1;
        for (int j = n; j > i; j--)
            if (t[j] < t[i])
                f2[i] = max(f2[i], f2[j] + 1);
    }
    // cout << f2[4] << '\n';

    int ans = 0;
    for (int i = 1; i <= n; i++) {
        ans = max(ans, f1[i] + f2[i] - 1);
    }

    printf("%d", n - ans);
	return 0;
}


D. 守望者的逃离#

传送门:水滴

题目大意:守望者要逃离一个岛,给出正整数 m,s,tm 代表守望者的初始法力值,s 表示守望者要逃离的路程,t 表示守望者要在这个时间里逃离。守望者支持以下操作:

  • 行走 17m ,耗时 1 秒。

  • 使用法术。行走 60m,耗时 1s,消耗 10 点法力。

  • 恢复 4 点法力,这时只能原地不动。

显然的,我们尽量让每次操作都是使用法术。所以我们先预处理出只使用法术的 dp 数组,然后再扫一遍,看看直接行走 17m 是否更优即可。中途如果发现已经到达就直接结束循环即可。

点击查看代码
#include <bits/stdc++.h>
#include <ctime>
#define ll long long
using namespace std;

const int maxn = 3e5 + 50;

int m, s, t;
int f[maxn], ans;

int main()
{
    scanf("%d%d%d", &m, &s, &t);

    for (int i = 1; i <= t; i++) {
        if (m >= 10) {
            f[i] = f[i - 1] + 60, m -= 10;
        } else {
            f[i] = f[i - 1], m += 4;
        }
    }

    for (int i = 1; i <= t; i++) {
        f[i] = max(f[i], f[i - 1] + 17);
        ans = max(ans, f[i]);
        if (f[i] > s) {
            printf("Yes\n%d", i);
            return 0;
        }
    }
    
    printf("No\n%d", ans);
	return 0;
}


E. 乌龟棋#

题目大意:给定 n 个正整数,表示数轴上每个点的权值。给定 m 个正整数,表示有 m 个跳点的次数。问怎样才能让整个数组中获得的权值最大。

考虑 dp。设计状态,f[a][b][c][d] 表示使用 a,b,c,d 个跳的次数获得的最大权值。考虑转移,先遍历每种跳的次数,因为只有放与不放,所以可以写出 a 的转移方程:

f[a][b][c][d]=max(f[a][b][c][d],f[a1][b][c][d]+q[g])

其他几个也同理。其中 g=1+a+b×2+c×3+d×4。记住 g 一定要加 1,因为遍历时是从 0 开始的。需要注意的是,写状态转移方程式一定要保证当前用的次数要 >0,否则会产生越界。

点击查看代码
#include <bits/stdc++.h>
#include <ctime>
#define ll long long
using namespace std;

const int maxn = 4e2 + 50, maxm = 1e2 + 50, maxf = 50;

int n, m;
int q[maxn], b[maxm];
int f[maxf][maxf][maxf][maxf];
int cnt[5];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%d", &q[i]);
    for (int i = 1; i <= m; i++) {
        scanf("%d", &b[i]);
        ++cnt[b[i]];
    }

    f[0][0][0][0] = q[1];
    for (int a = 0; a <= cnt[1]; a++)
        for (int b = 0; b <= cnt[2]; b++)
            for (int c = 0; c <= cnt[3]; c++)
                for (int d = 0; d <= cnt[4]; d++) {
                    int g = 1 + a + b * 2 + c * 3 + d * 4;
                    if (a) f[a][b][c][d] = max(f[a][b][c][d], f[a - 1][b][c][d] + q[g]);
                    if (b) f[a][b][c][d] = max(f[a][b][c][d], f[a][b - 1][c][d] + q[g]);
                    if (c) f[a][b][c][d] = max(f[a][b][c][d], f[a][b][c - 1][d] + q[g]);
                    if (d) f[a][b][c][d] = max(f[a][b][c][d], f[a][b][c][d - 1] + q[g]);
                }


    printf("%d\n", f[cnt[1]][cnt[2]][cnt[3]][cnt[4]]);
    return 0;
}

摆了八百年,还得继续写作业 /kk

F.饥饿的奶牛#

题目大意:给定 n 个区间,判断在区间没有重复的情况下最多能占多少格。

动态规划。先进行一个简单的设计:f[i] 表示前 i 个格子最多能占多少格。

显然的,f[j]f[j1],因为多选一个格子肯定不会比少选一个少。

最后转移式为:

f[i]=max(f[j1],f[j1]+j(i1))

最后要使用 vector 来优化空间即可通过此题。

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const int maxn = 3e6 + 50;

inline int read() {
	long long x = 0, w = 1;
	char ch = getchar ();
	for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return x * w;
}

vector <int> beg[maxn];
int n, mx, f[maxn];

int main()
{
    n = read();
    for (int i = 1; i <= n; i++) {
        int x = read(), y = read();
        beg[y].push_back(x - 1);
        mx = max(mx, y);
    }
    for (int i = 1; i <= mx; i++) {
        f[i] = f[i - 1];
        for (int j = 0; j < beg[i].size(); j++) {
            int b = beg[i][j];
            f[i] = max(f[i], f[b] + i - b);
        }
    }
    return printf("%d\n", f[mx]), 0;
}

作者:Aelt

出处:https://www.cnblogs.com/Aelt/p/18697743

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Aelt  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示