P1048 [NOIP 2005 普及组] 采药
题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
输入格式
第一行有 2 2 2 个整数 T T T( 1 ≤ T ≤ 1000 1 \le T \le 1000 1≤T≤1000)和 M M M( 1 ≤ M ≤ 100 1 \le M \le 100 1≤M≤100),用一个空格隔开, 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[i−1][j],dp[i−1][j−time[i]]+price[i])
时间不够
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j] dp[i][j]=dp[i−1][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 (N≤20) 个地窖,每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径。当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷,然后可以沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使某人能挖到最多的地雷。
输入格式
有若干行。
第 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 n−1 个数( 0 0 0 或 1 1 1),表示第一个地窖至第 2 2 2 个、第 3 3 3 个 … \dots … 第 n n n 个地窖有否路径连接。如第 3 3 3 行为 11000 ⋯ 0 11000\cdots 0 11000⋯0,则表示第 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 n−2 个数,表示第二个地窖至第 3 3 3 个、第 4 4 4 个 … \dots … 第 n n n 个地窖有否路径连接。
……
第 n + 1 n+1 n+1 行有 1 1 1 个数,表示第 n − 1 n-1 n−1 个地窖至第 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 n、m,表示生物种类 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 n≤2×103。
- 对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 2 × 1 0 5 1 \leq n \leq 2 \times 10^5 1≤n≤2×105, − 1 0 4 ≤ a i ≤ 1 0 4 -10^4 \leq a_i \leq 10^4 −104≤ai≤104。
做的最快的一集
话不多说直接贴代码,前缀和的题
数据是坑点,$ 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;
}