结点选择(ALGO-4)
(70分)
问题描述
有一棵 n 个节点的树,树上每个节点都有一个正整数权值。如果一个点被选择了,那么在树上和它相邻的点都不能被选择。求选出的点的权值和最大是多少?
输入
第一行包含一个整数 n 。
接下来的一行包含 n 个正整数,第 i 个正整数代表点 i 的权值。
接下来一共 n-1 行,每行描述树上的一条边。
输出
输出一个整数,代表选出的点的权值和的最大值。
样例输入
5
1 2 3 4 5
1 2
1 3
2 4
2 5
样例输出
12
样例说明
选择3、4、5号点,权值和为 3+4+5 = 12 。
数据规模与约定
对于20%的数据, n <= 20。
对于50%的数据, n <= 1000。
对于100%的数据, n <= 100000。
权值均为不超过1000的正整数。
参考:https://www.cnblogs.com/wowpH/p/11060764.html
题目解析
采用先序遍历的算法遍历整棵树。
当前子树的根结点可以选择,可以不选择。
(1)如果根结点不选择,那么它的某个子结点可不选择,也可选择。只需选取其中权值较大的那种加到根结点的权值中即可。
(2)如果根结点选择,那么它的所有子结点都不能选择。也就是将子结点不选择的那种的权值加到根结点权值中即可。
不选择当前子树的根结点,那么就将它的子结点(子树)i的权值较大的情况加到根结点中
weight[root][0] += Math.max(weight[i][0], weight[i][1]);
选择当前子树的根结点,那么将它的子结点(子树)i的不选择的权值加到根结点中
weight[root][1] += weight[i][0];
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
private Scanner sc;
private static final int MAX_N = 100001;
private int n;
private int[][] weight;// 权值
private List<List<Integer>> adjacencyList;// 邻接表
public Main() {
weight = new int[MAX_N][2];// 下标从1开始
sc = new Scanner(new InputStreamReader(System.in));
while (sc.hasNext()) {
adjacencyList = new ArrayList<List<Integer>>();// 头指针的线性表
input();
dfs(1, 0);// 1根节点,0无前驱结点
System.out.println(Math.max(weight[1][0], weight[1][1]));
}
sc.close();
}
private void input() {
n = sc.nextInt();// 结点数
adjacencyList.add(new ArrayList<Integer>());// 下标从1开始,这个不用
for (int i = 1; i <= n; i++) {
weight[i][0] = 0; // 初始化为0
weight[i][1] = sc.nextInt();// 输入权值
adjacencyList.add(new ArrayList<Integer>());// 创建头结点
}
int head, tail;// 弧的头尾
for (int i = 1; i < n; i++) {
tail = sc.nextInt();
head = sc.nextInt();
adjacencyList.get(tail).add(head);// 添加表结点
adjacencyList.get(head).add(tail);// 无向图,添加表结点
}
}
private void dfs(int root, int pre) {// root根,pre前驱结点
List<Integer> list = adjacencyList.get(root);// 当前链表
for (Integer i : list) {
if (i != pre) {// 非叶子结点,继续向下递归
dfs(i, root);
// 不选root点,选root子结点i最大的情况
weight[root][0] += Math.max(weight[i][0], weight[i][1]);
// 选root点,不选root子结点i的情况
weight[root][1] += weight[i][0];
}
}
}
public static void main(String[] args) {
new Main();
}
}
最短路(ALGO-5)
问题描述
给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环)。请你计算从1号点到其他点的最短路(顶点从1到n编号)。
输入
第一行两个整数n, m。
接下来的m行,每行有三个整数u, v, l,表示u到v有一条长度为l的边。
输出
共n-1行,第i行表示1号点到i+1号点的最短路。
样例输入
3 3
1 2 -1
2 3 -1
3 1 2
样例输出
-1
-2
数据规模与约定
对于10%的数据,n = 2,m = 2。
对于30%的数据,n <= 5,m <= 10。
对于100%的数据,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保证从任意顶点都能到达其他所有顶点。
参考:https://www.cnblogs.com/wowpH/p/11060761.html
题目解析
一、初始1号点与其他所有点距离为正无穷(47483647),
1号点与自己距离为0,
1号点与以1号点为弧尾的弧的弧头的距离为弧的长度。
二、松弛操作,有n个点,最多循环n-1次,每次遍历所有的边,
如果1号点与弧头的距离大于1号点与弧尾的距离加弧的长度,即
dis[arc[j].head] > dis[arc[j].tail] + arc[j].cost
那么就需要更新1号点与弧头的距离为较小的值,
否则遍历下一条边。
三、直到第i次循环,所有的边都没有进行松弛操作,退出循环。
四、输出1号点与其他点的距离即为最短路。
import java.util.Scanner;
//最短路
class Arc{
int tail;//弧尾
int head;//弧头
int cost;//弧的长度
}
public class Main {
public static void main(String[]args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();//定点数
int m=scan.nextInt();//边数
int dis[]=new int[n+1];//1号点与i点的距离,下标从1开始
Arc arc[]=new Arc[m];//弧
//初始化
dis[1]=0;//1号点到自身的距离为0
for(int i=2;i<=n;i++) {
dis[i]=47483647;//1号点与i点距离初始化为正无穷
}
for(int i=0;i<m;i++) {
arc[i]=new Arc();
arc[i].tail=scan.nextInt();
arc[i].head=scan.nextInt();
arc[i].cost=scan.nextInt();
//如果弧尾是1,那么1号点与该点的距离为弧长
if(arc[i].tail==1)
dis[arc[i].head]=arc[i].cost;
}
scan.close();
relaxation(arc,dis,n,m);
for(int i=2;i<=n;i++)
System.out.println(dis[i]);
}
//松弛操作
private static void relaxation(Arc arc[],int dis[],int n,int m) {
boolean flag;//是否进行松弛操作
for(int i=1;i<n;i++) {
flag=false;
for(int j=0;j<m;j++) {
if(dis[arc[j].head]>dis[arc[j].tail]+arc[j].cost) {
dis[arc[j].head]=dis[arc[j].tail]+arc[j].cost;
flag=true;
}
}
if(flag==false)
break;
}
}
}