Atcoder ABC341 F - Breakdown

Breakdown(分解)

时间限制:2s 内存限制:1024MB

【原题地址】

所有图片源自Atcoder,题目译文源自脚本Atcoder Better!

点击此处跳转至原题

【问题描述】

在这里插入图片描述

【输入格式】

在这里插入图片描述
在这里插入图片描述

【输出格式】

在这里插入图片描述

【样例1】

【样例输入1】

6 6
1 2
2 3
3 1
3 4
1 5
5 6
9 2 3 1 4 4
1 0 0 0 0 1

【样例输出1】

5

【样例说明1】

在这里插入图片描述

【样例2】

【样例输入2】

2 1
1 2
1 2
0 0

【样例输出2】

0

【样例说明2】

在这里插入图片描述

【样例3】

【样例输入3】

10 20
4 8
1 10
1 7
5 9
9 10
8 10
7 5
1 4
7 3
8 7
2 8
5 8
4 2
5 1
7 2
8 3
3 4
8 9
7 10
2 3
25 5 1 1 16 5 98 3 21 1
35 39 32 11 35 37 14 29 36 1

【样例输出3】

1380

【解题思路】

老汉使用到的是dp的解题方式

本题是求按照题目操作消除所有棋子时,最大的操作数。
本题消除该点的棋子时,会为下一关系点(通过边连接的点),放置一个棋子,我们只需求出在该点放置一个点时,完全消除它需要的最大操作数,最后再乘上该点拥有的棋子数就是最终答案。由于题目限制,只有当关系点的w总值小于该点的w值时,才可以进行放置棋子操作,这就有点类似背包求最大个数的题型,再加上w值大的点与w值小的点有着上下级关系,就有点树形的感觉,所以我们可以把该题看成一个树形背包问题,通过dfs遍历树,进行更新对应点的最大值。

代码注释有详细过程

【代码】

package ABC341_F_Breakdown;

import java.io.*;
import java.util.*;

public class Main {
	// 存储相互连接的两个点
	static Vector<Integer>[] g;
	// 存储对应点的w值
	static int[] w;
	// 存储在该点有一个棋子时,操作的最大次数
	static int[] dp;

	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
	static StreamTokenizer st = new StreamTokenizer(br);

	public static int readInt() throws IOException {
		st.nextToken();
		return (int) st.nval;
	}

	public static void main(String[] args) throws IOException {
		int n = readInt();
		int m = readInt();
		g = new Vector[n + 1];
		for (int i = 0; i <= n; i++) {
			g[i] = new Vector<Integer>();
		}
		// 存储相互连接的点
		for (int i = 1; i <= m; i++) {
			int u = readInt();
			int v = readInt();
			g[u].add(v);
			g[v].add(u);
		}
		w = new int[n + 1];
		dp = new int[n + 1];
		for (int i = 1; i <= n; i++) {
			w[i] = readInt();
		}
		// 当该点的值没有更新时,dfs更新一系列相关点的dp值
		for (int i = 1; i <= n; i++) {
			if (dp[i] == 0) {
				dfs(i, 0);
			}
		}
		// 存放总操作数
		long ans = 0;
		int[] a = new int[n + 1];
		for (int i = 1; i <= n; i++) {
			a[i] = readInt();
			// 求总操作数
			ans += (long) a[i] * dp[i];
		}
		pw.println(ans);
		pw.flush();
		pw.close();
		br.close();
	}

	/**
	 * 更新从该点开始,一系列相关点的dp值 注释:从头遍历到尾,再从尾部开始逐步向上跟新dp值
	 * 
	 * @param x  当前更新点
	 * @param fa 上一点
	 */
	public static void dfs(int x, int fa) {
		// dp值至少为1
		dp[x] = 1;
		// 存放消除该点棋子,分配给关系点后的最大操作次数
		int max = 0;
		// 存放对应背包容量时,最大的操作次数(背包容量为w[x]-1)
		int[] dp2 = new int[w[x]];
		// 遍历该点对应的关系点
		for (int i = 0; i < g[x].size(); i++) {
			int y = g[x].get(i);
			// 当遍历点为上一点时,跳过本次遍历
			if (y == fa)
				continue;
			// 当该点下一级关系点的dp未更新时,进行遍历更新
			if (w[x] > w[y] && dp[y] == 0)
				dfs(y, x);
			// 滚动数组,求背包最大值
			for (int j = w[x] - w[y] - 1; j >= 0; j--) {
				if (dp2[j] != 0 || j == 0) {
					dp2[j + w[y]] = Math.max(dp2[j + w[y]], dp2[j] + dp[y]);
					max = Math.max(max, dp2[j + w[y]]);
				}
			}
		}
		// 加上关系点的最大操作数即为该点最大操作数
		dp[x] += max;
	}
}

  • 8
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王老汉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值