01背包问题
[NOIP2005 普及组] 采药
题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
输入格式
第一行有
接下来的
输出格式
输出在规定的时间内可以采到的草药的最大总价值。
样例输入
70 3
71 100
69 1
1 2
样例输出
3
提示
【数据范围】
- 对于
的数据, ; - 对于全部的数据,
[!TIP]
01背包问题
特点:每个物品仅能使用一次
f[i][j]:
表示所有选法集合中,只从前i个物品中选,并且总体积≤j的选法的集合,它的值是这个集合中每一个选法的最大值状态转移方程
f[i][j] = max(f[i-1][j], f[i-1][j-v[i]]+w[i])
f[i-1][j]
:不选第i个物品的集合中的最大值
f[i-1][j-v[i]]+w[i]
:选第i个物品的集合,但是直接求不容易求所在集合的属性,先将第i个物品的体积减去,求剩下集合中选法的最大值集合如何划分
一般原则:不重不漏,不重不一定都要满足(一般求个数时要满足)
如何将现有的集合划分为更小的子集,使得所有子集都可以计算出来
//无优化版 #include <iostream> using namespace std; const int N = 1010; int n, m; int v[N], w[N]; int f[N][N]; int main() { cin >> n >> m; for(int i = 1; i <= n; i++) cin >> v[i] >> w[i]; for(int i = 1; i <= n; i++) { for(int j = 0; j <= m; j++) { f[i][j] = f[i-1][j]; if(j>=v[i]) f[i][j] = max(f[i][j], f[i-1][j-v[i]]+w[i]); } } cout << f[n][m] << endl; return 0; } //有优化版 //1. f[i] 仅用到了f[i-1]层 //2. j与j-v[i] 均小于j //3.若用到上一层的状态时,从大到小枚举, 反之从小到大 #include <iostream> using namespace std; const int N = 1010; int n, m; int v[N], w[N]; int f[N]; int main() { cin >> n >> m; for(int i = 1; i <= n; i++) cin >> v[i] >> w[i]; for(int i = 1; i <= n; i++) for(int j = m; j >= v[i]; j--) //01背包 一维写法 只能 逆序更新 // 完全背包 一维写法 只能 正序更新:for (int j = v[i]; j <= v[i]; j ++) f[j] = max(f[j], f[j-v[i]]+w[i]); cout << f[m] << endl; return 0; }
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int T, M,f[N],t[N], m[N];
int main()
{
scanf("%d %d", &T, &M);
for (int i = 1; i <= M; i++) {
// 第i种草药的时间和价值
scanf("%d %d", &t[i], &m[i]);
for (int j = T; j >= t[i]; j--) {
// 选择采摘该草药与不采摘该草药的最大价值
f[j] = max(f[j], f[j - t[i]] + m[i]);
}
}
printf("%d\n", f[T]);
return 0;
}
搭配购买
题目描述
明天就是母亲节了,电脑组的小朋友们在忙碌的课业之余挖空心思想着该送什么礼物来表达自己的心意呢?听说在某个网站上有卖云朵的,小朋友们决定一同前往去看看这种神奇的商品,这个店里有
输入格式
第一行输入三个整数,
第二行至
第
输出格式
一行,表示可以获得的最大价值。
样例输入
5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2
样例输出
1
提示
- 对于
的数据,满足 ; - 对于
的数据,满足 , ; - 对于
的数据,满足 ,
[!TIP]
用并查集将每一组搭配的云朵合并再看作01背包问题
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5;
int a, b, n, m, w, c[N], d[N], f[N] , dp[N], baka=0;
int find(int x) {//查找(并查集)
if (f[x] == x) {
return x;
}else{
return f[x] = find(f[x]);
}
}
int main()
{
scanf("%d %d %d", &n, &m, &w);
for (int i = 1; i <= n; i++)
{
scanf("%d %d", &c[i], &d[i]);
f[i] = i; //初始化
}
for (int i = 1; i <= m; i++) //搭配
{
scanf("%d %d", &a, &b);
int x = find(a), y = find(b);
if (x != y) {
c[y] += c[x]; d[y] += d[x];//合并
c[x] = d[x] = 0; //清空
f[x] = y;
}
}
for (int i = 1; i <= n; i++) {//预算w能获得的最大价值
for (int j = w; j >= c[i]; j--) {
dp[j] = max(dp[j], dp[j - c[i]] + d[i]);
}
}
for (int i = 1; i <= w; i++) {
baka = max(baka, dp[i]);
}
printf("%d\n", baka);
return 0;
}
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现