一,搭配购买
题目描述
明天就是母亲节了,电脑组的小朋友们在忙碌的课业之余挖空心思想着该送什么礼物来表达自己的心意呢?听说在某个网站上有卖云朵的,小朋友们决定一同前往去看看这种神奇的商品,这个店里有 nn 朵云,云朵已经被老板编号为 1,2,3,...,n1,2,3,...,n,并且每朵云都有一个价值,但是商店的老板是个很奇怪的人,他会告诉你一些云朵要搭配起来买才卖,也就是说买一朵云则与这朵云有搭配的云都要买,电脑组的你觉得这礼物实在是太新奇了,但是你的钱是有限的,所以你肯定是想用现有的钱买到尽量多价值的云。
输入格式
第一行输入三个整数,n,m,wn,m,w,表示有 nn 朵云,mm 个搭配和你现有的钱的数目。
第二行至 n+1n+1 行,每行有两个整数, c_i,d_ici,di,表示第 ii 朵云的价钱和价值。
第 n+2n+2 至 n+1+mn+1+m 行 ,每行有两个整数 u_i,v_iui,vi。表示买第 u_iui 朵云就必须买第 v_ivi 朵云,同理,如果买第 v_ivi 朵就必须买第 u_iui 朵。
输出格式
一行,表示可以获得的最大价值。
输入输出样例
输入 #1复制
5 3 10 3 10 3 10 3 10 5 100 10 1 1 3 3 2 4 2
输出 #1复制
1
说明/提示
- 对于 30\%30% 的数据,满足 1 \le n \le 1001≤n≤100;
- 对于 50\%50% 的数据,满足 1 \le n, w \le 10^31≤n,w≤103,1 \le m \le 1001≤m≤100;
- 对于 100\%100% 的数据,满足 1 \le n, w \le 10^41≤n,w≤104,0 \le m \le 5 \times 10^30≤m≤5×103。
分析:
1,题目有两个要点,第一个, ”一些云朵要搭配起来买才卖,也就是说买一朵云则与这朵云有搭配的云都要买“,这个条件的话,我们就要把需要搭配起来的云朵形成一个关系链,第二个,”钱是有限的,所以你肯定是想用现有的钱买到尽量多价值的云“,所以这就是求最优解,所以初步决定使用动态规划。
2,对于第一点,形成关系的话,我们一般就会使用并查集,将散的数据捆成一大个。
3,对于第二点,这个题目的关系就很想背包问题,在钱有限的情况下,找物品最大的价值。
4,所以我们使用并查集和动态规划,也就是01背包。
代码如下:
c
/*
*/
#include<stdio.h>
#include<math.h>
#include<string.h>
int a[10005],l[10005],r[10005],bl[10005],br[10005],k[10005];
int f(int x)
{
if(a[x]!=x)
return a[x]=f(a[x]);
return x;
}
int main()
{
int n,m,w;
scanf("%d%d%d",&n,&m,&w);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&l[i],&r[i]);
a[i]=i;
}
//for(int i=1;i<=n;i++)
//printf("%d",a[i]);
for(int i=1;i<=m;i++)
{
int r1,r2;
scanf("%d%d",&r1,&r2);
if(f(r1)!=f(r2))
{
l[f(r1)]+=l[f(r2)];
r[f(r1)]+=r[f(r2)];
a[f(r2)]=f(r1);
}
}
int t=0;
for(int i=1;i<=n;i++)
{
if(a[i]==i)
{
bl[++t]=l[i];
br[t]=r[i];
}
}
for(int i=1;i<=t;i++)
for(int j=w; j>=bl[i]; j--)
k[j]=k[j]>(k[j-bl[i]]+br[i])?k[j]:(k[j-bl[i]]+br[i]);
printf("%d",k[w]);
}
c++
#include <bits/stdc++.h>
using namespace std;
int n,m,w,tot;
int p[10000+10],v[10000+10],newp[10000+10],newv[10000+10],f[10000+10],father[10000+10];
void work() { //01背包
for(int i=1; i<=tot; i++)
for(int j=w; j>=newp[i]; j--) //滚动数组
f[j]=max(f[j],f[j-newp[i]]+newv[i]);
cout<<f[w];
}
int getfather(int x) {//
if(father[x]==x) return x;
father[x]=getfather(father[x]);//路径压缩
return father[x];
}
void hb(int x,int y) {//并查集合并
int fx=getfather(x);
int fy=getfather(y);
if(fx!=fy) {
v[fx]+=v[fy];
p[fx]+=p[fy];
father[fy]=fx;
}
}
int main() {
cin>>n>>m>>w;
int c,d;
for(int i=1; i<=n; i++) {
cin>>c>>d;
p[i]=c;
v[i]=d;
father[i]=i;
}
for(int i=1; i<=n; i++)
cout<<father[i];
for(int i=1; i<=m; i++) {
int x,y;
cin>>x>>y;
hb(x,y);
}
for(int i=1; i<=n; i++) if(father[i]==i)//取出合并好的物品
newp[++tot]=p[i],newv[tot]=v[i]; //将合并好的物品用新的数组装起来
work();//01背包
}
总结:
1,今天遇到的这个题目还是有点东西的,一开始全wa了,后来发现最短路径出了点问题。然后第二次也只过了两个,,就发现我试结果的两行代码没有删掉,,,,。
2,今天更多的是去看了几个dp的视频,dp其实一直都是难点,简单的dp问题不大,但是难一点就有点问题。
3,今天还搞了一下vscode编译器的一些配置,还有自定义快速提示,这个编译器比学校用的编译器要好用一点。