算法-並查集-買女裝

1.題目描述


小莊是個女裝愛好者,有一天他跑到女裝店去買女裝。商店裏有編號從1到n的n件衣物飾品,每一件都有固定的魅力加成值。
老闆告訴他,其中某些商品必須搭配購買,而且商品之間的搭配關係具有傳遞性,若A與B搭配且B與C搭配,則A與C也搭配。
小莊帶的錢有限,請你幫他找到魅力加成值總和最大的購買方案。

輸入
可能有多組輸入。
每組輸入第一行有三個數N、M、W(1<=N<=10000;0<=M<=5000,0<=W<=10000),
分別表示商店內有N件商品、老闆告訴了小莊M種搭配關係、小莊帶的錢數。 
接下來N行,每行有兩個數Vi、Ci,分別表示第i件商品的價格和魅力加成值。
接下來M行,每行有兩個數A和B,表示編號爲A和B的商品必須同時購買。

輸出
每組輸出有一行,爲最大魅力加成值。

樣例輸入
5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2

樣例輸出
1

 

2. 思路

     解題思路:首先識別出有聯繫的商品搭配關係(即找出一個個的搭配圈子),然後將每一個搭配圈裏的商品的價格與魅力值進行疊加。從此可以正式將整個問題轉化成了一個01揹包問題,即 現有k件價值爲Vi、魅力值爲Ci的商品(k代表搭配圈的個數,Vi、Ci則是第i個搭配圈中商品價格與魅力值的總和),在總金額W的情況下,如何購買才能使得魅力值總和最大?

     技術路線:先用並查集區分每一個搭配圈,並且把每一件商品的價格、魅力值加到其搭配圈的根節點上,然後將每個搭配圈的根節點作爲揹包元素採用動態規劃求解01揹包問題。

 

3. 代碼

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class F_買女裝 {
	static int n, m, w, res;
	static int[] root;

	// 尋找當前搭配圈中的根節點,輸入樣例中的1,2,3,4都屬於同一搭配圈,其根節點root[2]=0
	static int find(int x) {
		if (root[x] > 0) {
			x = root[x];
		}
		return x;
	}

	// 聯合搭配圈的節點,使其最終指向根節點
	static void union(int x, int y, int[] value, int[] charm) {
		int rx = find(x); // 得到x所在搭配圈的根節點
		int ry = find(y); // 得到y所在搭配圈的根節點

		if (rx == ry)	//若本來就在同一個搭配圈裏則不管
			return;
		
		root[rx] = ry;	//若不在同一圈子,那麼就把x所在的圈子添加到y所在的圈子中,ry爲根節點。
		
		//將新進搭配圈的商品 的價格與魅力值加到根節點上
		value[ry] += value[rx];	
		charm[ry] += charm[rx];
	}
	

	public static void main(String[] args) throws Exception {
		// TODO 自動生成的方法存根
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String line = br.readLine();

		n = Integer.parseInt(line.split(" ")[0]);
		m = Integer.parseInt(line.split(" ")[1]);
		w = Integer.parseInt(line.split(" ")[2]);

		int[] value = new int[n + 1];
		int[] charm = new int[n + 1];
		root = new int[n + 1];

		for (int i = 1; i <= n; i++) {
			line = br.readLine();
			value[i] = Integer.parseInt(line.split(" ")[0]);
			charm[i] = Integer.parseInt(line.split(" ")[1]);
		}
		
		// 1.構造並查集,尋找搭配圈
		for (int i = 1; i <= m; i++) {
			line = br.readLine();
			int a = Integer.parseInt(line.split(" ")[0]);
			int b = Integer.parseInt(line.split(" ")[1]);
			union(a, b, value, charm);
		}
		
		// 2.動規求解01揹包問題
		int[] m = new int[w+1];
		for(int i=1;i<=n;i++){
			if(root[i] == 0){	//尋找根節點,每找到一個根節點就開始重新定義dp數組
				for(int j=w;j>=value[i];j--){
					m[j] = Math.max(m[j], m[j-value[i]]+charm[i]);
				}
			}
		}
		
		System.out.print(m[w]);
	}

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章