P6082 树形DP

题目链接

https://www.luogu.com.cn/problem/P6082

题意

一棵树,每个点有收益(可负),经过一点获得其收益(多次经过不重复,一定获得不可放弃),每个点有最大停留次数(最小为2),起点为1,起点收益0,停留无限。问如何游览会最大化收益(可以在起点摸鱼不走)

思路

树形DP

dp[i]代表i为根子树最大收益,首先明确一个点的限制li[i],让他只能前往li[i]-1个子树。而一个点的收益就应当是所有子树中满足限制的最大的正DP值之和。
d p [ i ] = ∑ 1 l i [ i ] − 1 d p [ j ]      ( j 为 i 子 节 点 且 d p [ j ] > 0 ) dp[i]=\sum_{1}^{li[i]-1} dp[j] \ \ \ \ (j为i子节点且 dp[j]>0) dp[i]=1li[i]1dp[j]    (jidp[j]>0)
其中dp[j]从大到小排序



再考虑多种情况,稍微推一下就知道,假设限制是走t个子树,那么排序后的子树中,如果第t个和第t+1个数相等,那么一定是出现了不唯一(5,3,3,2,2 选3个是唯一的,选四个就不唯一了)。此外,如果出现了dp值为0,且可以被选择(排序后在li[i]-1范围内),那么他是可选可不选的一条路线那么一定不唯一。

以上

教训/收获

细节细节细节!!!

代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=100505;
	const int inf=0x3f3f3f3f;
	int n,m,k;
    int va[maxn],li[maxn];
    vector<int>e[maxn];
    int dp[maxn];
    int ans;
    bool uni=1;
    bool cmp(int a,int b){
        return dp[a]>dp[b];
    }
    void dfs(int x,int fa){
        for(auto y:e[x]){
            if(y==fa)   continue;
            dfs(y,x);
        }
        vector<int>v;
        for(auto y:e[x]){
            if(y==fa)   continue;
            if(dp[y]>=0)
                v.push_back(y);
        }
        sort(v.begin(),v.end(),cmp);
        int t=li[x];
        t--;
        if(t<v.size()&&v[t]==v[t-1])    uni=0;
        for(auto i :v){
            t--;
            dp[x]+=dp[i];
            if(dp[i]==0)    uni=0;
            if(!t) break;
        }
        dp[x]+=va[x];
    }
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
        cin>>n;
        for(int i=2;i<=n;i++)   cin>>va[i];
        for(int i=2;i<=n;i++)   cin>>li[i];
        va[1]=0;
        li[1]=inf;
        for(int i=1;i<n;i++){
            int u,v;
            cin>>u>>v;
            e[u].push_back(v);
            e[v].push_back(u);
        }
        dfs(1,-1);
        //for(int i=1;i<=n;i++)   cout<<i<<' '<<dp[i]<<endl;
        cout<<dp[1]<<endl;
        if(uni) cout<<"solution is unique"<<endl;
        else    cout<<"solution is not unique"<<endl;
	} 
						
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值