bzoj4472: [Jsoi2015]salesman(树形dp)

Description

某售货员小T要到若干城镇去推销商品,由于该地区是交通不便的山区,任意两个城镇
之间都只有唯一的可能经过其它城镇的路线。 小T 可以准确地估计出在每个城镇停留的净收
益。这些净收益可能是负数,即推销商品的利润抵不上花费。由于交通不便,小T经过每个
城镇都需要停留,在每个城镇的停留次数与在该地的净收益无关,因为很多费用不是计次收
取的,而每个城镇对小T的商品需求也是相对固定的,停留一次后就饱和了。每个城镇为了
强化治安,对外地人的最多停留次数有严格的规定。请你帮小T 设计一个收益最大的巡回方
案,即从家乡出发,在经过的每个城镇停留,最后回到家乡的旅行方案。你的程序只需输出
最大收益,以及最优方案是否唯一。方案并不包括路线的细节,方案相同的标准是选择经过
并停留的城镇是否相同。因为取消巡回也是一种方案,因此最大收益不会是负数。小T 在家
乡净收益是零,因为在家乡是本地人,家乡对小 T当然没有停留次数的限制。

Input

输入的第一行是一个正整数n(5<=n<=100000),表示城镇数目。城镇以1到n的数命名。小T 的家乡命
名为1。第二行和第三行都包含以空格隔开的n-1个整数,第二行的第i个数表示在城镇
i+1停留的净收益。第三行的第i个数表示城镇i+1规定的最大停留次数。所有的最大
停留次数都不小于2。接下来的n-1行每行两个1到n的正整数x,y,之间以一个空格
隔开,表示x,y之间有一条不经过其它城镇的双向道路。输入数据保证所有城镇是连通的。

Output

输出有两行,第一行包含一个自然数,表示巡回旅行的最大收益。如果该方案唯一,在
第二行输出“solution is unique”,否则在第二行输出“solution is not unique”。

Sample Input

9
-3 -4 2 4 -2 3 4 6
4 4 2 2 2 2 2 2
1 2
1 3
1 4
2 5
2 6
3 7
4 8
4 9

Sample Output

9
solution is unique
//最佳路线包括城镇 1,2, 4, 5, 9。

这道树形dp好清奇……

考虑一下,设$cnt[i]$表示在$i$点的最多停留次数,那么$cnt[i]-1$就是最多能进入的子树的个数(因为到达时必须停留一次)

然后发现子树里不管怎么走,对该点的停留次数的影响都是$1$,所以每一个子树里肯定要走出最优的方案

那么我们设$dp[v]$表示$v$这一整棵子树的最优方案的权值,考虑从$u$点如何选择才能使该点最优

首先自己必须选,然后把所有儿子的$dp$值排个序,取前$cnt[u]-1$个或一直取到第一个$dp$为0的值,不难发现没有方案会比他更优

然后存儿子的$dp$值的话可以用vector

现在的问题就是怎么判断方案是否唯一

首先,如果儿子的$dp$值的前$cnt[u]-1$个里有0,那么该点的最优方案肯定不唯一(因为0那个点可以选或不选)

其次,如果第$cnt[u]-1$和$cnt[u]$个都大于0且相等,那么方案也不唯一(因为这两个都可以选)

然后如果子树的方案不唯一,自己的方案也不唯一

所以用结构体存储子树的$dp$,分别记录最大权值和方案是否唯一,不断向上dp即可

交上去竟然1A了……

 1 //minamoto
 2 #include<bits/stdc++.h>
 3 using namespace std;
 4 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 5 char buf[1<<21],*p1=buf,*p2=buf;
 6 inline int read(){
 7     #define num ch-‘0‘
 8     char ch;bool flag=0;int res;
 9     while(!isdigit(ch=getc()))
10     (ch==‘-‘)&&(flag=true);
11     for(res=num;isdigit(ch=getc());res=res*10+num);
12     (flag)&&(res=-res);
13     #undef num
14     return res;
15 }
16 const int N=100005;
17 struct node{
18     int v,f;
19     node(){}
20     node(int v,int f):v(v),f(f){}
21     inline node operator +(const node &b)const
22     {return node(v+b.v,f|b.f|(b.v==0));}
23     inline bool operator <(const node &b)const
24     {return v>b.v;}
25     inline bool operator ==(const node &b)const
26     {return v==b.v;}
27     inline void operator +=(const node &b)
28     {*this=*this+b;}
29 }dp[N];
30 int head[N],Next[N<<1],ver[N<<1],tot;
31 inline void add(int u,int v){
32     ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
33 }
34 vector<node> val[N];int a[N],cnt[N],n,m;
35 void dfs(int u,int fa){
36     for(int i=head[u];i;i=Next[i]){
37         int v=ver[i];if(v==fa) continue;
38         dfs(v,u);
39         val[u].push_back(dp[v]);
40     }
41     dp[u]=node(a[u],0);
42     sort(val[u].begin(),val[u].end());
43     int i=0,s=val[u].size(),k=cnt[u]-1;
44     for(;i<s&&i<k&&val[u][i].v>=0;++i) dp[u]+=val[u][i];
45     if(i<s-1&&val[u][i].v>0&&val[u][i]==val[u][i+1]) dp[u].f=1;
46 }
47 int main(){
48 //    freopen("testdata.in","r",stdin);
49     n=read();
50     for(int i=2;i<=n;++i) a[i]=read();a[1]=0;
51     for(int i=2;i<=n;++i) cnt[i]=read();cnt[1]=n+1;
52     for(int i=1,u,v;i<n;++i)
53     u=read(),v=read(),add(u,v),add(v,u);
54     dfs(1,0);
55     printf("%d\n",dp[1].v);
56     puts(dp[1].f?"solution is not unique":"solution is unique");
57     return 0;
58 }

原文地址:https://www.cnblogs.com/bztMinamoto/p/9787004.html

时间: 2024-10-12 07:17:44

bzoj4472: [Jsoi2015]salesman(树形dp)的相关文章

JSOI Salesman 树形Dp

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

[BZOJ4472] [Jsoi2015]salesman(DFS/排序)

4472: [Jsoi2015]salesman Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 80  Solved: 45[Submit][Status][Discuss] Description 某售货员小T要到若干城镇去推销商品,由于该地区是交通不便的山区,任意两个城镇之间都只有唯一的可能经过其它城镇的路线. 小T 可以准确地估计出在每个城镇停留的净收益.这些净收益可能是负数,即推销商品的利润抵不上花费.由于交通不便,小T经过每个城镇都需要

【题解】 bzoj4472: [Jsoi2015]salesman (动态规划)

bzoj4472,懒得复制,戳我戳我 Solution: 体面意思:从\(1\)号节点出发,每到一个节点就必须停下,获得节点权值(每个节点只会获得一次),每个点有个规定的停留次数,求最大可获得多大权值,并且判断是否只有唯一的路线才能获得这个权值 直接\(dp\)储存子树最大获得权值就行,顺便要记录方案是否唯一,所以我们可以拿一个结构体来记录 \(dp\)权值思路:找出所有子树中前\(vis[i]-1\)大的节点权值(只选大于\(0\)的权值). \(dp\)方案思路: 1.如果有选择的节点是方案

[bzoj4472][树形DP] Salesman

题目 原地址 解说 刚看完这道题感觉还是挺乱的,可能那时候脑子不太清醒,一度觉得自己又要重拾Tarjan了.当然最后还是发觉应该用树形DP. (以下dp[u]代表以u为根的包括自己在内的子树的最大利润,bool g[u]表示u及其子树的方案数是否唯一,唯一则为0,否则为1,t[u]代表u的次数,v[u]代表u的价值) 计算最大利润确实挺简单.有点像之前做过的空调教室,但是多了次数限制和负数,但这不难处理.计算u的时候因为每个儿子在走完之后必须返回u来回到根节点,因此我们只能对儿子的dp值进行排序

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,