练习Day——16

结点选择(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;
		}
	}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值