BZOJ4711小奇挖矿 树形DP
Posted CQzhangyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ4711小奇挖矿 树形DP相关的知识,希望对你有一定的参考价值。
【BZOJ4711】小奇挖矿
Description
【题目背景】
小奇在喵星系使用了无限非概率驱动的采矿机,以至于在所有星球上都采出了一些矿石,现在它准备建一些矿石仓库并把矿石运到各个仓库里。
【问题描述】
喵星系有n个星球,标号为1到n,星球以及星球间的航线形成一棵树。所有星球间的双向航线的长度都为1。小奇要在若干个星球建矿石仓库,设立每个仓库的费用为K。对于未设立矿石仓库的星球,设其到一个仓库的距离为i,则将矿石运回的费用为Di。请你帮它决策最小化费用。
Input
第一行2个整数n,K。
第二行n-1个整数,D1,D2,…Dn-1,保证Di<=Di+1。
接下来n-1行,每行2个整数x,y,表示星球x和星球y存在双向航线。
n<=200,0<=K,Di<=100000
Output
输出一行一个整数,表示最小费用。
Sample Input
8 10
2 5 9 11 15 19 20
1 4
1 3
1 7
4 6
2 8
2 3
3 5
2 5 9 11 15 19 20
1 4
1 3
1 7
4 6
2 8
2 3
3 5
Sample Output
38
【样例解释】
在1,2号星球建立仓库。
【样例解释】
在1,2号星球建立仓库。
题解:前几天做了难么多奇葩的树形DP,然而这题还是没做出来。
n=200,那么应该是个二维或三维的状态。用f[x][y]表示x这个点运到y,并且x子树中的其它点都已经确定了运到哪,且y位置还没建仓库的最小费用。转移方法也是挺神的。
如果我们要用f[a][b]更新f[x][y](其中a是x的儿子),那么有几种情况:
1.b=y,由于我们先不用再y建仓库,所以直接用f[x][y]+f[a][b]更新。
2.b在a的子树中,那么在a的位置建一个即可,所以用f[x][y]+f[a][b]+K更新。
3.b不在a的子树中,这时,我们要么将y改成b,要么将b改成y,一定会使得答案变得更优,所以:不用更新!
最后答案就是min{f[1][x]+K}。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; int n,K,cnt,ans; int f[210][210],D[210],dep[210],p[210],q[210],head[210],to[410],next[410],fa[210],dis[210][210]; void init(int x) { p[x]=++p[0]; for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[x]) fa[to[i]]=x,dep[to[i]]=dep[x]+1,init(to[i]); q[x]=p[0]; } void dfs(int x) { f[x][0]=K; int i,j,k,y; for(j=1;j<=n;j++) f[x][j]=dis[x][j]; for(i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[x]) { y=to[i],dfs(y); for(j=1;j<=n;j++) { int fx=1<<30; for(k=1;k<=n;k++) { if(j==k) fx=min(fx,f[x][j]+f[y][k]); else if(p[k]>=p[y]&&p[k]<=q[y]) fx=min(fx,f[x][j]+f[y][k]+K); } f[x][j]=fx; } } } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret*f; } inline void add(int a,int b) { to[cnt]=b,next[cnt]=head[a],head[a]=cnt++; } int main() { n=rd(),K=rd(); int i,j,a,b; for(i=1;i<n;i++) D[i]=rd(); memset(head,-1,sizeof(head)); for(i=1;i<n;i++) a=rd(),b=rd(),add(a,b),add(b,a); init(1); for(i=1;i<=n;i++) for(j=i;j<=n;j++) { a=i,b=j; if(dep[a]<dep[b]) swap(a,b); while(dep[a]>dep[b]) a=fa[a]; while(a!=b) a=fa[a],b=fa[b]; dis[i][j]=dis[j][i]=D[dep[i]+dep[j]-2*dep[a]]; } memset(f,0x3f,sizeof(f)); dfs(1); ans=1<<30; for(i=1;i<=n;i++) ans=min(ans,f[1][i]); printf("%d",ans+K); return 0; }
以上是关于BZOJ4711小奇挖矿 树形DP的主要内容,如果未能解决你的问题,请参考以下文章