bzoj 4472: [Jsoi2015]salesman 树形dp

来源:互联网 发布:吾生吾生有涯而知无涯 编辑:程序博客网 时间:2024/05/22 00:25

题意

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

分析

恩树形dp一下就好了。
限制x的用处显然是只能往x-1个子树里面走。
设f[i]表示从i出发只走不超过x-1棵子树的最大价值。同时记录一下方案是否唯一即可。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;const int N=100005;int cnt,n,val[N],lim[N],last[N],a[N],a1,f[N],g[N];struct edge{int to,next;}e[N*2];void addedge(int u,int v){    e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;    e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;}bool cmp(int a,int b){    return f[a]>f[b];}void dfs(int x,int fa){    f[x]=val[x];    for (int i=last[x];i;i=e[i].next)        if (e[i].to!=fa) dfs(e[i].to,x);    a1=0;    for (int i=last[x];i;i=e[i].next)         if (e[i].to!=fa) a[++a1]=e[i].to;    sort(a+1,a+a1+1,cmp);    int p=0;    while (p<min(lim[x]-1,a1)&&f[a[p+1]]>=0) p++,f[x]+=f[a[p]],g[x]|=g[a[p]];    if (p<a1&&p>0&&f[a[p]]==f[a[p+1]]||f[a[p]]==0&&p>0) g[x]=1;}int main(){    scanf("%d",&n);    for (int i=2;i<=n;i++) scanf("%d",&val[i]);    for (int i=2;i<=n;i++) scanf("%d",&lim[i]);    for (int i=1;i<n;i++)    {        int x,y;        scanf("%d%d",&x,&y);        addedge(x,y);    }    lim[1]=n+1;    dfs(1,0);    printf("%d\n",f[1]);    if (g[1]) printf("solution is not unique");    else printf("solution is unique");    return 0;}
0 0
原创粉丝点击