[bzoj4472][树形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\n",dp[1]);
60     if(g[1]) printf("solution is not unique");
61     else printf("solution is unique");
62     return 0;
63 }

幸甚至哉,歌以咏志。

原文地址:https://www.cnblogs.com/DarthVictor/p/12633732.html

时间: 2024-09-30 06:26:31

[bzoj4472][树形DP] Salesman的相关文章

bzoj4472: [Jsoi2015]salesman(树形dp)

Description 某售货员小T要到若干城镇去推销商品,由于该地区是交通不便的山区,任意两个城镇之间都只有唯一的可能经过其它城镇的路线. 小T 可以准确地估计出在每个城镇停留的净收益.这些净收益可能是负数,即推销商品的利润抵不上花费.由于交通不便,小T经过每个城镇都需要停留,在每个城镇的停留次数与在该地的净收益无关,因为很多费用不是计次收取的,而每个城镇对小T的商品需求也是相对固定的,停留一次后就饱和了.每个城镇为了强化治安,对外地人的最多停留次数有严格的规定.请你帮小T 设计一个收益最大的

JSOI Salesman 树形Dp

题目链接 https://www.luogu.com.cn/problem/P6082 分析 这题一眼应该就能看出来是树形DP,题目中都多次暗示了,所以先把定义搞出来,最开始我跳了一个坑就是把状态定义成了\(DP[i][j]\),即在\(i\)号节点停留\(j\)次的最大收益,然后想啊想,就没有然后了..... 模拟几个样例发现停留次数有一个很特殊的性质,就是最多只能经过该点的儿子停留次数-1次,不然就回不了家了,但好像对我们这个转移没有什么帮助,回去读一遍题,发现点权可能为负?负的?那第二维状

HDU-2196 Computer (树形DP)

最近在看树形DP,这题应该是树形DP的经典题了,写完以后还是有点感觉的.之后看了discuss可以用树分治来做,以后再试一试. 题目大意 找到带权树上离每个点的最远点.︿( ̄︶ ̄)︿ 题解: 对于每一个点的最远点,就是以这个点为根到所有叶子节点的最长距离.但是如果确定根的话,除了根节点外,只能找到每个节点(度数-1)个子树的最大值,剩下一个子树是该节点当前的父亲节点. 所以当前节点的最远点在当前节点子树的所有叶子节点以及父亲节点的最远点上(当父亲节点的最远点不在当前节点的子树上时), 如果父亲节

UVA-01220 Party at Hali-Bula (树形DP+map)

题目链接:https://vjudge.net/problem/UVA-1220 思路: 树形DP模板题,求最大人数很简单,难点在于如何判断最大人数的名单是否有不同的情况: 解决方法是用一个数组f[manx][2]记录该节点是否出场的情况,为真时代表有多种情况; 具体讨论: 当父节点的值加上某个子节点的值时,他的f的情况也和该子节点一样: 当某个节点dp(i, 0) == dp(i, 1), 则该节点以及它的父节点也一定有多种情况(父节点必定取其中之一). Code: 1 #include<bi

HDU 1520 树形dp裸题

1.HDU 1520  Anniversary party 2.总结:第一道树形dp,有点纠结 题意:公司聚会,员工与直接上司不能同时来,求最大权值和 #include<iostream> #include<cstring> #include<cmath> #include<queue> #include<algorithm> #include<cstdio> #define max(a,b) a>b?a:b using nam

HDU2196 Computer(树形DP)

和LightOJ1257一样,之前我用了树分治写了.其实原来这题是道经典的树形DP,感觉这个DP不简单.. dp[0][u]表示以u为根的子树中的结点与u的最远距离 dp[1][u]表示以u为根的子树中的结点与u的次远距离 这两个可以一遍dfs通过儿子结点转移得到.显然dp[0][u]就是u的一个可能的答案,即u往下走的最远距离,还缺一部分就是u往上走的最远距离: dp[2][u]表示u往上走的最远距离 对于这个的转移,分两种情况,是这样的: dp[2][v] = max( dp[0][u]+w

hdu5593--ZYB&#39;s Tree(树形dp)

问题描述 ZYB有一颗N个节点的树,现在他希望你对于每一个点,求出离每个点距离不超过KK的点的个数. 两个点(x,y)在树上的距离定义为两个点树上最短路径经过的边数, 为了节约读入和输出的时间,我们采用如下方式进行读入输出: 读入:读入两个数A,B,令fai??为节点i的父亲,fa?1??=0;fa?i??=(A∗i+B)%(i−1)+1,i∈[2,N] . 输出:输出时只需输出N个点的答案的xor和即可. 输入描述 第一行一个整数TT表示数据组数. 接下来每组数据: 一行四个正整数N,K,A,

CF 219D Choosing Capital for Treeland 树形DP 好题

一个国家,有n座城市,编号为1~n,有n-1条有向边 如果不考虑边的有向性,这n个城市刚好构成一棵树 现在国王要在这n个城市中选择一个作为首都 要求:从首都可以到达这个国家的任何一个城市(边是有向的) 所以一个城市作为首都,可能会有若干边需要改变方向 现在问,选择哪些城市作为首都,需要改变方向的边最少. 输出最少需要改变方向的边数 输出可以作为首都的编号 树形DP 先假定城市1作为首都 令tree(i)表示以i为根的子树 dp[i]表示在tree(i)中,若以i为首都的话,需要改变的边数 第一次

HDU 1011 Starship Troopers(树形dp+背包)

Starship Troopers Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 13109    Accepted Submission(s): 3562 Problem Description You, the leader of Starship Troopers, are sent to destroy a base of