• 【DP(换根 DP)】AcWing 287. 积蓄程度


    比较好想的换根 DP,但是有坑点。

    我的做法需要的代码行数应该比较少。。去掉头部就 40 行左右。

    分析

    约定 \(f[u]\) 表示以 \(u\) 为根节点的时候的最大流量,\(w(u, v)\)\(u, v\) 之间的边权。

    首先考虑根节点为 \(u\) 的时候如何统计 \(f[u]\)(也就是统计子树 \(u\) 的结果):

    • \(u\) 的子节点 \(son[u]\) 能够提供的流量为 \(f[son[u]]\)
    • 因此,相应能够为 \(f[u]\) 提供的贡献为 \(\min(f[son[u]], w(u, son[u]))\)

    这样,以 \(1\) 为根节点,我们就能够通过一次 \(dfs\)\(f[1]\) 的结果处理出来。

    需要注意的是,为了保证源点的流量是无穷,我们应该加一个特判:当遇到源点(叶节点)时,首先将其 \(f\) 值设为 \(\inf\),最后在 \(dfs\) 后为了保证 \(f\) 值的正确性我们再将其设为 \(0\)


    下面考虑换根,将所有节点为根的时候的结果再次使用一次 \(dfs\) 统计出来。

    那么转移的过程是:

    • 设现在已经处理好了 \(f[u]\)
    • 考虑使用父节点 \(u\)\(f\) 值更新 \(f[son[u]]\),换句话说就是考虑 \(u\) 能为 \(son[u]\) 提供的流量是多少。
    • 那么,\(f[son[u]]\) 需要加上的是 \(u\) 点减去 \(son[u]\) 所在的子树的贡献 \(\min(f[son[u]], w(u, son[u])\),也就是:\(f[son[u]] += f[u] - \min(f[son[u]], w(u, son[u]))\)

    但是这里有一个问题:如果父节点 \(u\) 可以作为源点(也就是其度数为 \(1\)),会使 \(u\)\(son[u]\) 提供的流量为 \(\min(w(u, son[u]), \inf) = w(u, son[u])\)。但这个情况其实只会出现于:\(u\) 为根节点 \(1\)\(1\) 的度数为 \(1\) 的情况,对此特判就好了。

    // Problem: 积蓄程度
    // Contest: AcWing
    // URL: https://www.acwing.com/problem/content/289/
    // Memory Limit: 64 MB
    // Time Limit: 1000 ms
    // 
    // Powered by CP Editor (https://cpeditor.org)
    
    #include<bits/stdc++.h>
    using namespace std;
    
    #define debug(x) cerr << #x << ": " << (x) << endl
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define dwn(i,a,b) for(int i=(a);i>=(b);i--)
    #define pb push_back
    #define all(x) (x).begin(), (x).end()
    
    #define x first
    #define y second
    using pii = pair<int, int>;
    using ll = long long;
    
    inline void read(int &x){
        int s=0; x=1;
        char ch=getchar();
        while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
        while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
        x*=s;
    }
    
    const int N=4e5+5, INF=0x3f3f3f3f;
    
    vector<pii> g[N];
    int f[N];
    int n;
    
    void dfs1(int u, int fa){
    	f[u]=(g[u].size()==1 && u!=1? INF: 0);
    	for(auto [go, w]: g[u]){
    		if(go==fa) continue;
    		dfs1(go, u);
    		f[u]+=min(w, f[go]);
    	}
    }
    
    void dfs2(int u, int fa){
    	for(auto [go, w]: g[u]){
    		if(go==fa) continue;
    		f[go]+=min(w, (u==1 && g[u].size()==1? INF: f[u])-min(w, f[go]));
    		dfs2(go, u);
    	}
    }
    
    int main(){
    	int T; cin>>T;
    	while(T--){
    		cin>>n;
    		rep(i,1,n-1){
    			int u, v, w; read(u), read(v), read(w);
    			g[u].pb({v, w}), g[v].pb({u, w});
    		}
    		dfs1(1, 0);
    		rep(i,1,n) if(f[i]==INF) f[i]=0;
    		dfs2(1, 0);
    		int res=0;
    		rep(i,1,n) res=max(res, f[i]);
    		cout<<res<<endl;
    		
    		rep(i,1,n<<1) g[i].clear();
    	}	
    	return 0;
    }
    
  • 相关阅读:
    Linux内核分析--系统调用【转】
    Linux slab分配器【转】
    简化指令与复杂指令的区别【转】
    冯诺依曼体系结构与哈佛体系结构的区别【转】
    bzero, memset ,setmem 区别【转】
    写一个标准宏MIN,输入两个参数,返回较小的
    红黑树(一)之原理和算法的详细分析【转】
    socket心跳包机制总结【转】
    Linux文件时间详解ctime、mtime、atime【转】
    【转】图文并茂 Ubuntu使用Thunderbird方法指南
  • 原文地址:https://www.cnblogs.com/Tenshi/p/15954483.html
Copyright © 2020-2023  润新知