BZOJ 4711: 小奇挖矿 树形DP
这个状态的定义非常难想吧...
$f[x][y]$ 表示以 $x$ 为根的子树中,其余点全部移到了应该移动到的位置,也建设了应该建设的仓库,而 $x$ 移动到 $y$ 的最小代价.
这里是事前钦定 $y$ 不建立仓库的.
那么,我们考虑如何从 $x$ 的儿子转移到 $x$:
若 $f[son][b]$ 中 $b$ 与 $y$ 相等,则 $f[x][y]=f[x'][y]+f[son][y]$
若 $f[son][b]$ 中 $b$ 与 $y$ 不相等,且 $b$ 在 $son$ 的子树中,则直接让 $son$ 去 $b$ 就好了.
最后一种情况:$b$ 与 $y$ 不相等,且 $b$ 在 $son$ 的子树外,那么你发现这个一定没有 $b$ 与 $y$ 相等的优,按照第二种方式转移就行(反正不会起贡献)
code:
#include <cstdio> #include <string> #include <cstring> #define N 205 #define ll long long using namespace std; void setIO(string s) { freopen((s+".in").c_str(),"r",stdin); } int n,K,edges; int val[N],dis[N][N],hd[N],to[N<<1],nex[N<<1],f[N][N]; void add(int u,int v) { nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; } void dfs(int u,int ff) { for(int i=hd[u];i;i=nex[i]) if(to[i]!=ff) dfs(to[i],u); for(int i=1;i<=n;++i) { f[u][i]=dis[u][i]; for(int j=hd[u];j;j=nex[j]) { int y=to[j]; int tmp=f[y][i]; if(y==ff) continue; for(int k=1;k<=n;++k) tmp=min(tmp,f[y][k]+K); f[u][i]+=tmp; } } } void getdis(int pr,int d,int u,int ff) { for(int i=hd[u];i;i=nex[i]) if(to[i]!=ff) dis[pr][to[i]]=val[d],getdis(pr,d+1,to[i],u); } int main() { // setIO("input"); int i,j; scanf("%d%d",&n,&K); for(i=1;i<n;++i) scanf("%d",&val[i]); for(i=1;i<n;++i) { int x,y; scanf("%d%d",&x,&y); add(x,y),add(y,x); } for(i=1;i<=n;++i) getdis(i,1,i,0); dfs(1,0); int cur=1e9; for(i=1;i<=n;++i) cur=min(cur,f[1][i]); printf("%d\n",cur+K); return 0; }