链接:经商
来源:牛客网
题目描述
小d是一个搞房地产的土豪。每个人经商都有每个人经商的手段,当然人际关系是需要放在首位的。
小d每一个月都需要列出来一个人际关系表,表示他们搞房地产的人的一个人际关系网,但是他的精力有限,对应他只能和能够接触到的人交际。比如1认识2,2认识3,那么1就可以接触3进行交际,当然1和2也可以交际。
小d还很精明,他知道他和谁交际的深获得的利益大,接下来他根据自己的想法又列出来一个利益表,表示他和这些人交际需要耗用多少精力,能够获得的利益值为多少。
小d想知道,他在精力范围内,能够获得的利益值到底是多少。
设定小d自己的编号为1.并且对应一个人的交际次数限定为1.
输入描述:
本题包含多组输入,第一行输入一个数t,表示测试数据的组数 每组数据的第一行输入三个数,N,M,C,表示这个人际关系网一共有多少个人,关系网的关系数,以及小d的精力值 接下来N-1行,每行两个数ai,bi。这里第i行表示和编号为i+1的人交际需要花费ai的精力,能够获得的利益值为bi。 再接下来M行,每行两个数x,y,表示编号为x的人能够和编号为y的人接触。 t<=50 2<=N<=10000 1<=M<=10*N 1<=ai,bi<=10 1<=C<=500 1<=x,y<=N
输出描述:
输出包含一行,表示小d能够获得的最大利益值
示例1
输入
1 5 3 7 5 10 3 2 4 3 1 100 1 2 2 3 1 4
输出
10
解题思路
要知道小d在精力范围内,能够获得的利益值到底是多少,就得先知道小d究竟可以和谁进行交际。根据题目描述,就需要首先使用并查集来找到小d可以交际的人。
有关并查集的知识大家可以看这篇博客:Java并查集详解(附Leetcode 547.省份数量讲解)。
import java.util.*;
/**
* Main
*
* @author Beau Wang
*/
public class Main {
public static void main(String[] args) {
// 并查集
int[] parent = new int[n + 1];
// 初始化
for (int i = 1; i < parent.length; i++) {
parent[i] = i;
}
// 合并
for (int[] r : relation) {
parent[find(parent, r[0])] = find(parent, r[1]);
}
}
public static int find(int[] parent, int x) {
while (parent[x] != x) {
x = parent[x];
}
return parent[x];
}
}
我们先遍历一遍,将小d可以联系到的人存到链表中。然后将精力和花费转换为重量数组和价值数组。
ArrayList<Integer> list = new ArrayList<>();
for (int i = 2; i <= n; i++) {
if (find(parent, 1) == find(parent, i)) {
list.add(i);
}
}
int[] weight = new int[list.size()];
int[] value = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
weight[i] = data[list.get(i)][0];
value[i] = data[list.get(i)][1];
}
此时这个问题就变成了一个典型的01背包问题。
int[][] dp = new int[value.length][c + 1];
// 初始化
for (int i = weight[0]; i <= c; i++) {
dp[0][i] = value[0];
}
// 遍历物品
for (int i = 1; i < value.length; i++) {
// 第i个物品在背包容量为j时的最大价值
for (int j = 0; j <= c; j++) {
if (j >= weight[i]) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
最后 dp[value.length - 1][c] 即为所求结果。
完整代码
import java.util.*;
/**
* Main
*
* @author Beau Wang
*/
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int t = scanner.nextInt();
while (t-- > 0) {
int n = scanner.nextInt();
int m = scanner.nextInt();
int c = scanner.nextInt();
int[][] data = new int[n + 1][2];
for (int i = 2; i <= n; i++) {
data[i][0] = scanner.nextInt();
data[i][1] = scanner.nextInt();
}
int[][] relation = new int[m][2];
for (int i = 0; i < m; i++) {
relation[i][0] = scanner.nextInt();
relation[i][1] = scanner.nextInt();
}
// 并查集
int[] parent = new int[n + 1];
// 初始化
for (int i = 1; i < parent.length; i++) {
parent[i] = i;
}
// 合并
for (int[] r : relation) {
parent[find(parent, r[0])] = find(parent, r[1]);
}
ArrayList<Integer> list = new ArrayList<>();
for (int i = 2; i <= n; i++) {
if (find(parent, 1) == find(parent, i)) {
list.add(i);
}
}
int[] weight = new int[list.size()];
int[] value = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
weight[i] = data[list.get(i)][0];
value[i] = data[list.get(i)][1];
}
int[][] dp = new int[value.length][c + 1];
// 初始化
for (int i = weight[0]; i <= c; i++) {
dp[0][i] = value[0];
}
// 遍历物品
for (int i = 1; i < value.length; i++) {
// 第i个物品在背包容量为j时的最大价值
for (int j = 0; j <= c; j++) {
if (j >= weight[i]) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
System.out.println(dp[value.length - 1][c]);
}
}
public static int find(int[] parent, int x) {
while (parent[x] != x) {
x = parent[x];
}
return parent[x];
}
}