这个题要运用背包,并查集的知识,但是用二维dp数组过不了,会超限,得用一维滚动数组。
【题目描述】
明天就是母亲节了,电脑组的小朋友们在忙碌的课业之余挖空心思想着该送什么礼物来表达自己的心意呢?听说在某个网站上有卖云朵的,小朋友们决定一同前往去看看这种神奇的商品,这个店里有 nn 朵云,云朵已经被老板编号为 1,2,3,...,n1,2,3,...,n,并且每朵云都有一个价值,但是商店的老板是个很奇怪的人,他会告诉你一些云朵要搭配起来买才卖,也就是说买一朵云则与这朵云有搭配的云都要买,电脑组的你觉得这礼物实在是太新奇了,但是你的钱是有限的,所以你肯定是想用现有的钱买到尽量多价值的云。
【输入】
第一行输入三个整数,n,m,w,表示有 n 朵云,m 个搭配和你现有的钱的数目。
第二行至 n+1 行,每行有两个整数, ci,di,表示第 i 朵云的价钱和价值。
第 n+2 至 n+1+m 行 ,每行有两个整数 ui,vi。表示买第 ui 朵云就必须买第 vi 朵云,同理,如果买第 vi 朵就必须买第 ui 朵。
【输出】
一行,表示可以获得的最大价值。
样例输入
5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2
样例输出
1
解题思路
数字是有点多,传入物品的价格和价值时,用一个结构体存储;
输入云朵之前的关系时,传入并查集函数,找到搭配购买的云朵。
要解决动态规划和并查集的结合,首先写到动态规划时,怎么找散乱在数组中应该要搭配在一起的云朵呢?在并查集之后找肯定不行,因为不知道要多少个云朵团体,而且每找一个云朵团体就要把云朵全部循环一次。
所以可以尝试在并查集的同时,把同一祖宗的云朵,按照 “ 靠左原则 ”,“ 擒贼先擒王 ”的原则,把当前得同一首领,也就是说可能同一祖宗下,可能有首领,他们反正是同一祖宗了,搭配的时候要一起买。并查集的时候就一并累加起来,最终祖宗云朵(也就是 f[x]=x 时)对应的结构体,就是要搭配购买的 “ 大物件 ” ,动态规划时,就把这些当做一个物品处理。
代码如下:
#include<stdio.h>
int w,d=1;
int f[10005];//并查集数组初始化
int q[10005],r[10005];//存储合成的大物件的价格和价值
int dp[10005];//一维滚动数组
struct node//定义结构体存储
{
int x;//存放价格
int y;//存放价值
};
struct node k[10005];
int getf(int b)//找祖宗
{
if(f[b]==b)//如果他自己就是的话,传出他本身
return b;
else//不是的话,就接着找
return f[b]=getf(f[b]);
}
void merge(int a,int b)
{
//t1和 t2分别是传入的 a和 b的祖宗
int t1=getf(a);
int t2=getf(b);
if(t1!=t2)//如果 t1和 t2不相等,就按照靠左原则,把 t2的祖先改为 t1
{
//因为买云朵是要按照搭配一起买的,在这里就把祖宗一样的价格和价值合在一起,相当于一个大物件
k[t1].x=k[t1].x+k[t2].x;
k[t1].y=k[t1].y+k[t2].y;
f[t2]=t1;
}
return ;
}
int max(int a,int b)//输出较大数的函数
{
if(a>b)
return a;
else
return b;
}
void fun()//动态规划找最大价值
{
int i,j;
for(i=1;i<=d;i++)//遍历 d件大物品
{
for(j=w;j>=q[i];j--)//从价值最大开始
dp[j]=max(dp[j],dp[j-q[i]]+r[i]);
}
printf("%d",dp[w]);//一维 dp数组最后一个的价值最大
}
int main()
{
int n,m,a,b,i;
scanf("%d %d %d",&n,&m,&w);
for(i=1;i<=n;i++)
{
f[i]=i;//将并查集初识化
scanf("%d %d",&k[i].x,&k[i].y);//输入价格和价值
}
for(i=1;i<=m;i++)
{
scanf("%d %d",&a,&b);//输入搭配的关系
merge(a,b);//并查集
}
for(i=1;i<=n;i++)
{
//如果当前的数是祖宗,说明他是一个大物件,循环找出有多少大物件
if(f[i]==i)
{
q[d]=k[i].x;
r[d]=k[i].y;
d++;
}
}
fun();//动态规划
return 0;
}