zoukankan      html  css  js  c++  java
  • [ZJOI2007] 时态同步(树形dp)

    传送门

    很好的一道树形dp的题目
    • 根据所给题意可以总结为:从激励点出发求到所有叶子节点时间相同的最小次数

    • 由于要找最小次数,考虑贪心的方法,要使次数尽量小,也就是要使子节点的公共边尽可能的大。

    • 有一点可以肯定的是一定要知道时间最长是多少,然后其他树枝才能根据这个时间求最小值,而这个最长时间是很容易求得的。

    • 求完最长时间后,考虑其他树枝,如何判断公共树枝要加多少呢?方法就是知道从它的最下面的叶子节点到它这里最远要多长时间。知道最远时间后,就能求得一个时间差,要让这个节点内的所有点的权值都相同,这个差也就是将这个节点最大的一个子节点依次减去其他节点的权值,然后将这些差值累加起来。最后的答案就是这个统计的差值。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 5e5 + 10;
    typedef long long ll;
    bool vis[N];
    ll n, tot, s, head[N], dp[N], maxn[N], ans;
    
    struct Edge{
    	ll v, w, next;
    }edge[N * 2];
    
    void add(ll u, ll v, ll w){
    	edge[tot].v = v;
    	edge[tot].w = w;
    	edge[tot].next = head[u];
    	head[u] = tot ++;
    }
    
    void dfs(int now, int fa){
    	for(int i = head[now]; i != -1; i = edge[i].next){
    		int v = edge[i].v;
    		int w = edge[i].w;
    		if(v == fa)
    			continue;
    		dfs(v, now);
    		dp[now] = max(dp[now], dp[v] + w);		//找出当前节点的时间最长的一个节点
    	}
    	for(int i = head[now]; i != -1; i = edge[i].next){
    		int v = edge[i].v;
    		int w = edge[i].w;
    		if(v == fa)
    			continue;
    		ans += (dp[now] - (dp[v] + w));			//统计差值
    	}
    }
    int main(){
    	scanf("%lld%lld", &n, &s);	
    	memset(head, -1, sizeof head);
    	for(int i = 1; i < n; i ++){
    		ll a, b, w;
    		scanf("%lld%lld%lld", &a, &b, &w);
    		add(a, b, w);
    		add(b, a, w);
    	}
    
    	dfs(s, 0);
    
    	printf("%lld
    ", ans);
    	return 0;
    } 
    
  • 相关阅读:
    linux常用命令
    练习00004
    python学习第六天
    练习00003
    练习00002
    python学习第四天
    练习00001
    Linux_安装mysql踩坑日记
    Linux_更改远程登录端口以及禁止root用户登录
    redis_基础_基本使用
  • 原文地址:https://www.cnblogs.com/pureayu/p/14984382.html
Copyright © 2011-2022 走看看