动态规划练习

P1048 [NOIP 2005 普及组] 采药

题目描述

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

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

输入格式

第一行有 2 2 2 个整数 T T T 1 ≤ T ≤ 1000 1 \le T \le 1000 1T1000)和 M M M 1 ≤ M ≤ 100 1 \le M \le 100 1M100),用一个空格隔开, T T T 代表总共能够用来采药的时间, M M M 代表山洞里的草药的数目。

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

输出格式

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

输入 #1

70 3
71 100
69 1
1 2

输出 #1

3

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

如果时间足够

d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − t i m e [ i ] ] + p r i c e [ 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])

时间不够

d p [ i ] [ j ] = d p [ 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) 个地窖,每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径。当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷,然后可以沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使某人能挖到最多的地雷。

输入格式

有若干行。

1 1 1 行只有一个数字,表示地窖的个数 N N N

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

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

3 3 3 行有 n − 1 n-1 n1 个数( 0 0 0 1 1 1),表示第一个地窖至第 2 2 2 个、第 3 3 3 … \dots n n n 个地窖有否路径连接。如第 3 3 3 行为 11000 ⋯ 0 11000\cdots 0 110000,则表示第 1 1 1 个地窖至第 2 2 2 个地窖有路径,至第 3 3 3 个地窖有路径,至第 4 4 4 个地窖、第 5 5 5 … \dots n n n 个地窖没有路径。

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

……

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

输出格式

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

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

输入输出样例 #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 非常急,所以你只有 1 1 1 秒的时间。

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

输入格式

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

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

输出格式

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

输入输出样例 #1

输入 #1

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

输出 #1

5

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

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

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

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

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

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

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

#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 最大子段和

题目描述

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

输入格式

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

第二行有 n n n 个整数,第 i i i 个整数表示序列的第 i i i 个数字 a i a_i ai

输出格式

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

输入输出样例 #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},其和为 4 4 4

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

做的最快的一集

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

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

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值