• [BZOI4472][树形DP] Salesman


    题目

    原地址

    解说

    刚看完这道题感觉还是挺乱的,可能那时候脑子不太清醒,一度觉得自己又要重拾Tarjan了。当然最后还是发觉应该用树形DP。

    (以下dp[u]代表以u为根的包括自己在内的子树的最大利润,bool g[u]表示u及其子树的方案数是否唯一,唯一则为0,否则为1,t[u]代表u的次数,v[u]代表u的价值)

    计算最大利润确实挺简单。有点像之前做过的空调教室,但是多了次数限制和负数,但这不难处理。计算u的时候因为每个儿子在走完之后必须返回u来回到根节点,因此我们只能对儿子的dp值进行排序,选取前t[u]-1个,并且遇到负数立即停止因为接下来选的负数只能使总价值变小。

    那么怎么处理方案是否唯一呢?

    我们开一个bool数组g表示u及其子树的方案数是否唯一。显然,只有以下3种情况下方案数不唯一:

    某个取得的儿子dp值为0(选不选都可以)

    某个取得的儿子g值为1(在这颗子树中有不同的路径)

    下个未选的儿子和最后选择的儿子f值相同(可以替换)(我写的时候忽略了这一点但是还是A了,大约是数据太弱了)

    那么就从根开始递归一遍就可以了哦,最后看dp[1]和g[1]就可以了。

    (部分实在想不上来的做法参考了DZN大佬的,谢谢啦~)

    代码

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn=100000+5,Inf=2147483647;
     4 inline int read(){
     5    int s=0,w=1;
     6    char ch=getchar();
     7    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
     8    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
     9    return s*w;
    10 }
    11 int n,head[maxn],tot,v[maxn],t[maxn],dp[maxn];
    12 bool g[maxn];
    13 struct node{
    14     int to,next;
    15 }e[2*maxn];
    16 void Add(int a,int b){
    17     e[tot].to=b;
    18     e[tot].next=head[a];
    19     head[a]=tot;
    20     tot++;
    21 }
    22 void dfs(int x,int fa){
    23     priority_queue<pair<int,int> > q;//按照dp值大小排序 
    24     for(int i=head[x];i;i=e[i].next){
    25         int v=e[i].to;
    26         if(v==fa) continue;
    27         dfs(v,x);
    28         q.push(make_pair(dp[v],v));
    29     }
    30     int num=0,sum=0,judge=0;
    31     while(!q.empty()&&num<t[x]-1){
    32         if(q.top().first<0) break;//出现拖后腿的立即停止 
    33         if(q.top().first==0){//有0说明方案不为1,
    34         //且0后面要么是0要么是负的,都无法做贡献,直接退 
    35             judge=1;
    36             break;
    37         }
    38         sum+=q.top().first;
    39         if(g[q.top().second]==1) judge=1;
    40         q.pop();
    41         num++;
    42     }
    43     dp[x]=sum+v[x];
    44     g[x]=judge;
    45 }
    46 int main(){
    47     tot=1;
    48     t[1]=Inf;//注意自己家可以走无数次 
    49     n=read();
    50     for(int i=2;i<=n;i++) v[i]=read();
    51     for(int i=2;i<=n;i++) t[i]=read();
    52     for(int i=1;i<=n-1;i++){
    53         int x,y;
    54         x=read(); y=read();
    55         Add(x,y);
    56         Add(y,x);
    57     }
    58     dfs(1,0);
    59     printf("%d
    ",dp[1]);
    60     if(g[1]) printf("solution is not unique");
    61     else printf("solution is unique");
    62     return 0;
    63 }
    View Code

    幸甚至哉,歌以咏志。

  • 相关阅读:
    js 提示框的实现---组件开发之(一)
    js 原型链
    js 哈希路由原理实现
    js 弹窗的实现
    js 滑动门的实现
    Delphi IDFtp用法,包含断点续传
    memortstream Base64编码和filestream base64编码不同
    Delphi另一个多线程函数:BeginThread用法
    delphi 讲的比较详细的多线程(推荐)
    多线程简单实用
  • 原文地址:https://www.cnblogs.com/DarthVictor/p/12633732.html
Copyright © 2020-2023  润新知