【一隻蒟蒻的刷題歷程】--- 【洛谷】 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章