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]);
}
}