习题:Beavermuncher-0xFF(树DP)

题目

传送门

思路

算是比较明显的一个树DP

\(dp_i\)表示遍历以i为根节点的子树能吃的最多的海狸

合并儿子节点的DP值只需要累加就可以了

注意可能有根节点海狸不够的情况,所以我们还要把儿子节点的DP值排个序

但是注意,这里并不保证i号节点的海狸被吃完了,所以转移的时候我们需要将这个因素考虑进去

我们只需要统计一下,再更新即可

代码

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n,s;
int val[100005];
long long dp[100005],ans;
vector<int> g[100005];
/*
以i为根节点的子树,从i出发回到i,根节点至少剩余1只海狸的方案数
*/
bool cmp(const int &a,const int &b)
{
	return dp[a]>dp[b];
}
void dfs(int u,int fa)
{
	if(g[u].size()==1&&g[u].front()==fa)
		return;
	long long t;
	if(u==s)
    	t=val[u];
    else
    	t=val[u]-1;
    long long s=0;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(v!=fa)
        {
            dfs(v,u);
        }
    }
    sort(g[u].begin(),g[u].end(),cmp);
    for(int i=0;i<g[u].size();i++)
    {
    	int v=g[u][i];
    	if(v!=fa)
    	{ 
            if(t==0)
                break;
    		dp[u]+=dp[v];
            if(dp[v])
            {
				t--;
        		dp[u]+=2;
        		s+=val[v]-1;
			}
			else
				s+=val[v];
           
		}
	}
    dp[u]=dp[u]+2*min(t,s);
    val[u]=t-min(t,s)+1;
    
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>val[i];
    for(int i=1,u,v;i<n;i++)
    {
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    cin>>s;
    dfs(s,-1);
    cout<<dp[s];
    return 0;
}
posted @ 2020-07-31 11:30  loney_s  阅读(136)  评论(0)    收藏  举报