【洛谷】 P1455 搭配购买(并查集+01背包)

题目描述

明天就是母亲节了,电脑组的小朋友们在忙碌的课业之余挖空心思想着该送什么礼物来表达自己的心意呢?听说在某个网站上有卖云朵的,小朋友们决定一同前往去看看这种神奇的商品,这个店里有 nnn 朵云,云朵已经被老板编号为 1,2,3,…,n1,2,3,…,n1,2,3,…,n,并且每朵云都有一个价值,但是商店的老板是个很奇怪的人,他会告诉你一些云朵要搭配起来买才卖,也就是说买一朵云则与这朵云有搭配的云都要买,电脑组的你觉得这礼物实在是太新奇了,但是你的钱是有限的,所以你肯定是想用现有的钱买到尽量多价值的云。

输入格式

第一行输入三个整数,n,m,wn,m,wn,m,w,表示有 nnn 朵云,mmm 个搭配和你现有的钱的数目。

第二行至 n+1n+1n+1 行,每行有两个整数, ci,dic_i,d_ici​,di​,表示第 iii 朵云的价钱和价值。

第 n+2n+2n+2 至 n+1+mn+1+mn+1+m 行 ,每行有两个整数 ui,viu_i,v_iui​,vi​。表示买第 uiu_iui​ 朵云就必须买第 viv_ivi​ 朵云,同理,如果买第 viv_ivi​ 朵就必须买第 uiu_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\%30% 的数据,满足 1≤n≤1001 \le n \le 1001≤n≤100;
对于 50%50\%50% 的数据,满足 1≤n,w≤1031 \le n,w \le 10^31≤n,w≤103,1≤m≤1001 \le m \le 1001≤m≤100;
对于 100%100\%100% 的数据,满足 1≤n≤1041 \le n \le 10^41≤n≤104,0≤m≤5×1030 \le m \le 5 \times 10^30≤m≤5×103。

别开O2优化,别开O2优化,别开O2优化 (不开ac,开了就60,不知道什么原理,去网上搜了一下发现这种情况还不少,,,,,,,,,)
思路:买一个东西,就得买和ta有关的东西,并且是相互的,例如买了1就得买3,买了3就得买1,因此把要有联系的物品都放在一个集合里,买集合中任意一个物品,都得买这个集合中其他所有物品。为了更好判断买某个集合的所有物品的price与身上总的钱w之间的关系,可以把一个集合中的price和value都累加到一个数组元素中。最后这个问题就演变成了有w钱,要用不超过已有的钱w买到最大价值的东西,就是01背包思想,买或不买,不断更新最大值。

很少做到这种能用到两个算法的题目,感觉这种题目很有意思,也是很好的题目,

代码附上

#include <iostream>
#include <algorithm>
#include <string>
#include <queue>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=10050;

int father[maxn];


void init()   //初始化
{
    for(int i=1;i<maxn;i++)
     father[i]=i;   //自己为自己的父亲结点,根节点的特征
}

int find(int x)  //找祖宗
{
	return x==father[x]?x:father[x]=find(father[x]);
	/*如果father[x]=x 自己是自己的父亲,即为根节点,直接返回
	 否则,路径压缩,把x父辈的所有的结点(不止父亲)都变成根结点的儿子结点*/
}

void Union(int a,int b) //合并集合
{
	int fa=find(a);
	int fb=find(b);
	if(fa!=fb)
	 father[fa]=fb;
}

int main() 
{
	init();
    int n,m,w,ans=0;
    int price[maxn],value[maxn];  
	cin>>n>>m>>w;
	for(int i=1;i<=n;i++)
	{
		cin>>price[i]>>value[i];    //价格 价值
	} 
	
	int a,b;
	while(m--)
	{
		cin>>a>>b;  
		Union(a,b);
	}
	
	int dp[maxn];  //dp
	 
	for(int i=1;i<=n;i++)
	{
		if(i!=father[i]) /*不是根的话,把同一个集合的price和value累加到
		                   根的那个数组 */
		{
			price[find(i)]+=price[i];  //累加到根的那个数组
	    	value[find(i)]+=value[i];
		    price[i]=0;    //加过就更新为0,避免之后遍历到时再次加入 
		    value[i]=0;
		}
	}
	
	for(int i=1;i<=n;i++)   //01背包 放或不放
		for(int v=w;v>=price[i];v--)
           dp[v] = max(dp[v],dp[v-price[i]]+value[i]);
    
    cout<<dp[w];
	return 0;
}
发布了11 篇原创文章 · 获赞 15 · 访问量 321

猜你喜欢

转载自blog.csdn.net/weixin_45260385/article/details/105314597