luogu P1455 搭配购买
01背包
/*二维
#include <iostream>
#include <algorithm>
const int N = 1010;
int v[N], w[N], f[N][N];
using namespace std;
int main()
{
int n, m; 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 = 1; j <= m; j++)
{
f[i][j] = f[i - 1][j]; //先更新到上一层的状态,因为下一行不进行的话,f[i][j]就未被赋值
if(j >= v[i]) f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
//考虑是否放入物品i,就要看放入之后它能不能让价值的最大值变大
//假设放入,用去掉第i个物品的最大值加上第i个物品的价值(递推)
}
}
cout << f[n][m];
}
*/
//一维
#include <iostream>
#include <algorithm>
const int N = 1010;
int v[N], w[N], f[N];
using namespace std;
int main()
{
int n, m; cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> v[i] >> w[i];
for(int i = 1; i <= n; i++) //因为背包内最终个数是多少都不重要,只要<=n个即可,由于f[i][j] = max(f[i - 1)[j], f[i - 1][j - v[i] + w[i]), 求放第i个f[j]只需要用到上一层放第i - 1个的f[j], 所以可以写成一维
{
for(int j = m; j >= v[i]; j--) f[j] = max(f[j], f[j - v[i]] + w[i]); //只有j >= v[i]时才会更新状态,< v[i]就是没有放入,体积不变,价值也不变
/*for(int j = v[i]; j <= m; j++) 因为j是递增的,所以计算到f[i][j]的时候
肯定已经计算了f[i][j - v[i]],由于现在数组是一维的,f[j - v[i]] = f[i][j - v[i]],
但是我们需要的是f[i - 1][j - v[i]],所以不能从前往后遍历,应该从后往前,那么这样小的数就来不及更新
*/
}
cout << f[m];
}
搭配购买
题目描述
明天就是母亲节了,电脑组的小朋友们在忙碌的课业之余挖空心思想着该送什么礼物来表达自己的心意呢?听说在某个网站上有卖云朵的,小朋友们决定一同前往去看看这种神奇的商品,这个店里有 \(n\) 朵云,云朵已经被老板编号为 \(1,2,3,...,n\),并且每朵云都有一个价值,但是商店的老板是个很奇怪的人,他会告诉你一些云朵要搭配起来买才卖,也就是说买一朵云则与这朵云有搭配的云都要买,电脑组的你觉得这礼物实在是太新奇了,但是你的钱是有限的,所以你肯定是想用现有的钱买到尽量多价值的云。
输入格式
第一行输入三个整数,\(n,m,w\),表示有 \(n\) 朵云,\(m\) 个搭配和你现有的钱的数目。
第二行至 \(n+1\) 行,每行有两个整数, \(c_i,d_i\),表示第 \(i\) 朵云的价钱和价值。
第 \(n+2\) 至 \(n+1+m\) 行 ,每行有两个整数 \(u_i,v_i\)。表示买第 \(u_i\) 朵云就必须买第 \(v_i\) 朵云,同理,如果买第 \(v_i\) 朵就必须买第 \(u_i\) 朵。
输出格式
一行,表示可以获得的最大价值。
样例
5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2
1
提示
对于 \(100\%\) 的数据,满足 \(1 \le n, w \le 10^4\),\(0 \le m \le 5 \times 10^3\)。
思路
将必须搭配购买的用并查集
合并到一起,将价值和价钱相加,就变成了01背包
代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e4 + 10;
int p[N], v[N], w[N], f[N];
int find(int x);
void uniona(int x, int y);
int main()
{
int n, m, q; cin >> n >> m >> q;
for(int i = 1; i <= n; i++) p[i] = i, cin >> v[i] >> w[i];
while(m--)
{
int x, y; cin >> x >> y;
int p1 = find(x), p2 = find(y);
if(p1 != p2) uniona(p1, p2);
}
for(int i = 1; i <= n; i++)
{
if(v[i])
for(int j = q; j >= v[i]; j--) f[j] = max(f[j], f[j - v[i]] + w[i]);
}
cout << f[q];
}
int find(int x)
{
int a = x;
while(a != p[a]) a = p[a];
int b = x, c;
while(b != a)
{
c = p[b];
p[b] = a;
b = c;
}
return a;
}
void uniona(int x, int y)
{
p[x] = y;
v[y] += v[x], w[y] += w[x]; //合并价钱和价值
v[x] = 0, w[x] = 0; //将被合并的置0,遍历时就能跳过
}