zoukankan      html  css  js  c++  java
  • 0x50 动态规划(0x54 树形DP)例题3:积蓄程度(题解)

    题意

    神仙题

    【题目描述】
     有一个树形的水系,由 N-1 条河道和 N 个交叉点组成。
     我们可以把交叉点看作树中的节点,编号为 1~N,河道则看作树中的无向边。
     每条河道都有一个容量,连接 x 与 y 的河道的容量记为 c(x,y)。
     河道中单位时间流过的水量不能超过河道的容量。
     有一个节点是整个水系的发源地,可以源源不断地流出水,我们称之为源点。
     除了源点之外,树中所有度数为 1 的节点都是入海口,可以吸收无限多的水,我们称之为汇点。
     也就是说,水系中的水从源点出发,沿着每条河道,最终流向各个汇点。
     在整个水系稳定时,每条河道中的水都以单位时间固定的水量流向固定的方向。
     除源点和汇点之外,其余各点不贮存水,也就是流入该点的河道水量之和等于从该点流出的河道水量之和。
     整个水系的流量就定义为源点单位时间发出的水量。
     在流量不超过河道容量的前提下,求哪个点作为源点时,整个水系的流量最大,输出这个最大值。  
      【输入格式】
     输入第一行包含整数T,表示共有T组测试数据。
     每组测试数据,第一行包含整数N。
     接下来N-1行,每行包含三个整数x,y,z,表示x,y之间存在河道,且河道容量为z。
     节点编号从1开始。  
      【输出格式】
     每组数据输出一个结果,每个结果占一行。
     数据保证结果不超过2^31−1。  
      【数据范围】
    N≤2*10^5  
      【输入样例】
    1
     5
     1 2 11
     1 4 13
     3 4 5
     4 5 10   
      【输出样例】
    26 
    

    思路

    我的妈呀,如果以前没做仓鼠找sugar II估计我还真做不出这道题目。

    看到这种找根且还带树形DP的题目,脑子里面要马上树立一个正确的价值观,就是我们先固定一个根。

    很明显,我们只要固定了一个根的话,算出他的流量不是手到擒来?

    我们设(f[i])表示这棵子树以(i)为汇点的话,那么流量是多少,随便DP一下就可以算出(f[1]),同时我们又发现,我们貌似是可以(O(1))转移的!

    在这里插入图片描述

    把当树根为(1)的情况转移到树根为(2)的情况貌似只用(O(1)),即减去(2)(1)的贡献,然后把(1)(2)的贡献加到(2),然后我们就可以算出树根为(2)的值了,那么我们再DFS一遍不就可以(O(n))解决了吗?

    当然根节点如果只有一个儿子的话那么转移到儿子后他的(f)(inf)

    #include<cstdio>
    #include<cstring>
    #define  N  210000
    #define  M  410000
    #define  inf  2147483647
    using  namespace  std;
    struct  node
    {
    	int  y,c,next;
    }a[M];int  len,last[N];
    inline  void  ins(int  x,int  y,int  c){len++;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;}
    int  f[N]/*表示的是以1为根的话,那么这个点的水流是多少*/,dp[N]/*表示以他为根时的答案。*/;
    int  siz[N],dep[N];
    int  n;
    inline  int  mymin(int  x,int  y){return  x<y?x:y;}
    inline  int  mymax(int  x,int  y){return  x>y?x:y;}
    void  dfs1(int  x,int  fa)
    {
    	siz[x]=1;
    	for(int  k=last[x];k;k=a[k].next)
    	{
    		int  y=a[k].y;
    		if(y!=fa)
    		{
    			dep[y]=dep[x]+1;
    			dfs1(y,x);
    			f[x]+=mymin(f[y],a[k].c);siz[x]+=siz[y];
    		}
    	}
    	if(siz[x]==1)f[x]=inf;
    }
    void  dfs2(int  x,int  fa)//表示以x为根的答案。 
    {
    	dp[x]=f[x];//索性继承 
    	for(int  k=last[x];k;k=a[k].next)
    	{
    		int  y=a[k].y;
    		if(y!=fa)
    		{
    			int  now=f[x];
    			if(siz[x]-siz[y]==1  &&  dep[x]==0/*在根节点*/)f[x]=inf;
    			else  f[x]-=mymin(f[y],a[k].c);
    			if(siz[y]==1)f[y]=mymin(f[x],a[k].c);//特判这个点是不是叶子节点 
    			else  f[y]+=mymin(f[x],a[k].c);
    			dfs2(y,x);
    			if(siz[y]==1)f[y]=inf;
    			else  f[y]-=mymin(f[x],a[k].c);
    			f[x]=now;
    		}
    	}
    }
    int  main()
    {
    	int  T;scanf("%d",&T);
    	while(T--)
    	{
    		len=0;memset(last,0,sizeof(last));
    		memset(f,0,sizeof(f));
    		memset(dp,0,sizeof(dp));//暴力初始化
    		scanf("%d",&n);
    		for(int  i=1;i<n;i++)
    		{
    			int  x,y,z;scanf("%d%d%d",&x,&y,&z);
    			ins(x,y,z);ins(y,x,z);
    		}
    		dfs1(1,0);
    		dfs2(1,0);
    		int  ans=0;
    		for(int  i=1;i<=n;i++)ans=mymax(dp[i],ans);
    		printf("%d
    ",ans);
    	}
    	return  0;
    }
    
  • 相关阅读:
    leetcode 131. Palindrome Partitioning
    leetcode 526. Beautiful Arrangement
    poj 1852 Ants
    leetcode 1219. Path with Maximum Gold
    leetcode 66. Plus One
    leetcode 43. Multiply Strings
    pytorch中torch.narrow()函数
    pytorch中的torch.repeat()函数与numpy.tile()
    leetcode 1051. Height Checker
    leetcode 561. Array Partition I
  • 原文地址:https://www.cnblogs.com/zhangjianjunab/p/11714515.html
Copyright © 2011-2022 走看看