动态规划练习

P1048 [NOIP 2005 普及组] 采药

题目描述

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是辰辰,你能完成这个任务吗?

输入格式

第一行有 222 个整数 TTT1≤T≤10001 \le T \le 10001T1000)和 MMM1≤M≤1001 \le M \le 1001M100),用一个空格隔开,TTT 代表总共能够用来采药的时间,MMM 代表山洞里的草药的数目。

接下来的 MMM 行每行包括两个在 111100100100 之间(包括 111100100100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出格式

输出在规定的时间内可以采到的草药的最大总价值。

输入 #1

70 3
71 100
69 1
1 2

输出 #1

3

一道经典01背包,主要还是列状态转移方程,这道题状态转移方程大致为

如果时间足够

dp[i][j]=max(dp[i−1][j],dp[i−1][j−time[i]]+price[i])dp[i][j] = max(dp[i-1][j], dp[i-1][j-time[i]] + price[i])dp[i][j]=max(dp[i1][j],dp[i1][jtime[i]]+price[i])

时间不够

dp[i][j]=dp[i−1][j]dp[i][j] = dp[i-1][j]dp[i][j]=dp[i1][j]

#include<bits/stdc++.h>
using namespace std;
int a[1002], b[1002], dp[1002][1002];
int main()
{
	int T, m;
	cin >> T >> m;
	for (int i = 1; i <= m; i++)
		cin >> a[i] >> b[i];
	for (int i = 1; i <= m; i++)
	{
		for (int j = 1; j <= T; j++)
		{
			if (j >= a[i]) {
				dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - a[i]] + b[i]);
			}
			else
				dp[i][j] = dp[i - 1][j];
		}
	}
	cout << dp[m][T];
	return 0;
}

P2196 [NOIP 1996 提高组] 挖地雷

题目描述

在一个地图上有 N (N≤20)N\ (N \le 20)N (N20) 个地窖,每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径。当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷,然后可以沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使某人能挖到最多的地雷。

输入格式

有若干行。

111 行只有一个数字,表示地窖的个数 NNN

222 行有 NNN 个数,分别表示每个地窖中的地雷个数。

333 行至第 N+1N+1N+1 行表示地窖之间的连接情况:

333 行有 n−1n-1n1 个数(000111),表示第一个地窖至第 222 个、第 333…\dotsnnn 个地窖有否路径连接。如第 333 行为 11000⋯011000\cdots 0110000,则表示第 111 个地窖至第 222 个地窖有路径,至第 333 个地窖有路径,至第 444 个地窖、第 555…\dotsnnn 个地窖没有路径。

444 行有 n−2n-2n2 个数,表示第二个地窖至第 333 个、第 444…\dotsnnn 个地窖有否路径连接。

……

n+1n+1n+1 行有 111 个数,表示第 n−1n-1n1 个地窖至第 nnn 个地窖有否路径连接。(为 000 表示没有路径,为 111 表示有路径)。

输出格式

第一行表示挖得最多地雷时的挖地雷的顺序,各地窖序号间以一个空格分隔,不得有多余的空格。

第二行只有一个数,表示能挖到的最多地雷数。

输入输出样例 #1

输入 #1

5
10 8 4 7 6
1 1 1 0
0 0 0
1 1
1

输出 #1

1 3 4 5
27

太弱了做了好久,dp太差了

看题解这道题可以dfs爆搜

主要是列状态转移以及怎么存路径,由于数据太弱了所以全存也不会超时

代码如下

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int N;
int a[22];
int b[22][22];
int dp[22];
queue<int> dpq[22];

int main() {
    cin >> N;
    for (int i = 0; i < N; i++)cin >> a[i], dp[i] = a[i], dpq[i].push(i + 1);
    for (int i = 0; i < N - 1; i++) {
        for (int j = 0; j < N - i - 1; j++) {
            cin >> b[i][j];
        }
    }
    for (int i = 0; i < N - 1; i++) {
        for (int j = 0; j < N - 1 - i; j++) {
            if (b[i][j]) {
                if (dp[i + j + 1] < dp[i] + a[i + j + 1]) {
                    dpq[i + j + 1] = dpq[i];//路径优化
                    dpq[i + j + 1].push(i + j + 2);//存路径
                }
                dp[i + j + 1] = max(dp[i + j + 1], dp[i] + a[i + j + 1]);//状态转移方程
            }
        }
    }
    int maxx = 0;
    int k;
    for (int i = 0; i < N; i++) {
        if (dp[i] > maxx)k = i;
        maxx = max(dp[i], maxx);
    }
    while (!dpq[k].empty()) {
        cout << dpq[k].front() << ' ';
        dpq[k].pop();
    }
    cout << endl;
    cout << maxx;
    return 0;
}

P4017 最大食物链计数

题目背景

你知道食物链吗?Delia 生物考试的时候,数食物链条数的题目全都错了,因为她总是重复数了几条或漏掉了几条。于是她来就来求助你,然而你也不会啊!写一个程序来帮帮她吧。

题目描述

给你一个食物网,你要求出这个食物网中最大食物链的数量。

(这里的“最大食物链”,指的是生物学意义上的食物链,即最左端是不会捕食其他生物的生产者,最右端是不会被其他生物捕食的消费者。)

Delia 非常急,所以你只有 111 秒的时间。

由于这个结果可能过大,你只需要输出总数模上 801120028011200280112002 的结果。

输入格式

第一行,两个正整数 n、mn、mnm,表示生物种类 nnn 和吃与被吃的关系数 mmm

接下来 mmm 行,每行两个正整数,表示被吃的生物A和吃A的生物B。

输出格式

一行一个整数,为最大食物链数量模上 801120028011200280112002 的结果。

输入输出样例 #1

输入 #1

5 7
1 2
1 3
2 3
3 5
2 5
4 5
3 4

输出 #1

5

一开始以为是最长食物链的长度,我说怎么这么简单

结果是个拓扑的题,从来没做过最后看了题解才写出来

大概意思就是从入度为 000 的点到出度为 000 的点的路径总和可以用拓扑的方法写

这里面 111 就是入度为 000 的点,先定义入度为 000 的点的路径总和为 111

那么 111 能到的点路径总和就加上 111 的路径总和的值

就是 d[i]+=d[k]d[i] += d[k]d[i]+=d[k]

接着把 111 弹出队列, 继续查入度为 000 的节点,代码如下

#include<bits/stdc++.h>
using namespace std;
#define long long
int n, m;
int num = 0;
int b[5005][5005];
int ru[5005];
int chu[5005];
int f[5005];
queue<int> q;
int main() {
	cin >> n >> m;
	for (int i = 1; i <= m; i++) {
		int x,y;
		cin >> x >> y;
		b[x][y] = 1;
		chu[x]++;
		ru[y]++;
	}
	for (int i = 1; i <= n; i++) {
		if (ru[i] == 0) {
			q.push(i);
			f[i] = 1;
		}
	}
	while (!q.empty()) {
		int k = q.front();
		q.pop();
		for (int i = 1; i <= n; i++) {
			if (!b[k][i])continue;
			f[i] += f[k];
			f[i] %= 80112002;
			ru[i]--;
			if (ru[i] == 0) {
				if (chu[i] == 0) {
					num += f[i];
					num %= 80112002;
				}
				q.push(i);
			}
		}
	}
	cout << num;

	return 0;
}

P1115 最大子段和

题目描述

给出一个长度为 nnn 的序列 aaa,选出其中连续且非空的一段使得这段和最大。

输入格式

第一行是一个整数,表示序列的长度 nnn

第二行有 nnn 个整数,第 iii 个整数表示序列的第 iii 个数字 aia_iai

输出格式

输出一行一个整数表示答案。

输入输出样例 #1

输入 #1

7
2 -4 3 -1 2 -4 3

输出 #1

4

说明/提示

样例 1 解释

选取 [3,5][3, 5][3,5] 子段 {3,−1,2}\{3, -1, 2\}{3,1,2},其和为 444

数据规模与约定
  • 对于 40%40\%40% 的数据,保证 n≤2×103n \leq 2 \times 10^3n2×103
  • 对于 100%100\%100% 的数据,保证 1≤n≤2×1051 \leq n \leq 2 \times 10^51n2×105−104≤ai≤104-10^4 \leq a_i \leq 10^4104ai104

做的最快的一集

话不多说直接贴代码,前缀和的题

数据是坑点,$ a_i $ 的最小值是 −104-10^4104

#include<bits/stdc++.h>
using namespace std;
int n;
int a[200005];
int b[200005];
int main() {
	cin >> n;
	int maxx = -1e9;//数据中最小值不是0是-1e4
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		b[i] = a[i];
		if (b[i - 1] > 0)b[i] += b[i - 1];
		maxx = max(maxx, b[i]);
	}
	cout << maxx;
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值