bzoj 4711 小奇挖矿 —— 树形DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4711
就是树形DP,然而也想了半天才把转移想清楚;
f[x][j][0] 表示 x 去上面 j 步的仓库,f[x][j][1] 表示 x 去子树内下去 j 步的仓库而且整个子树都算好了;
f[x][j][0] 就是 d[j] 加上儿子们的 f[u][...][1] 与 f[u][j+1][0] 的最小值;
f[x][j][1] 就是选一个儿子加它的 f[u][j-1][1],其它儿子是 f[u][...][1] 与 f[u][j+1][0] 的最小值;
f[x][0][1] 要特殊处理一下;
然后一交秒WA,又改了改,似乎也没改什么,就A了...总之注意边界。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int const xn=205,inf=0x3f3f3f3f; int n,K,d[xn],ans,f[xn][xn][2],mn[xn]; int hd[xn],ct,nxt[xn<<1],to[xn<<1]; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar(); return f?ret:-ret; } void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;} void dfs(int x,int fa) { for(int i=hd[x],u;i;i=nxt[i]) { if((u=to[i])==fa)continue; dfs(u,x); } for(int j=0;j<n;j++) { if(!j)f[x][j][0]=K; else f[x][j][0]=d[j]; for(int i=hd[x],u;i;i=nxt[i]) { if((u=to[i])==fa)continue; // for(int k=0;k<n;k++)ret=min(ret,f[u][k][1]); int ret=min(mn[u],f[u][j+1][0]); f[x][j][0]+=ret; } if(!j) { f[x][j][1]=K; for(int i=hd[x],u;i;i=nxt[i]) { if((u=to[i])==fa)continue; int ret=min(f[u][1][0],mn[u]); // for(int k=0;k<n;k++)ret=min(ret,f[u][k][1]); f[x][j][1]+=ret; } } else { f[x][j][1]=inf; for(int i=hd[x],v;i;i=nxt[i]) { if((v=to[i])==fa)continue; int r=f[v][j-1][1]+d[j]; for(int l=hd[x],u;l;l=nxt[l]) { if((u=to[l])==fa||u==v)continue; int ret=min(f[u][j+1][0],mn[u]); // for(int k=0;k<n;k++)ret=min(ret,f[u][k][1]); r+=ret; } f[x][j][1]=min(f[x][j][1],r); } } mn[x]=min(mn[x],f[x][j][1]); } } int main() { n=rd(); K=rd(); for(int i=1;i<n;i++)d[i]=rd(); for(int i=1,x,y;i<n;i++) { x=rd(); y=rd(); add(x,y); add(y,x); } memset(f,0x3f,sizeof f); memset(mn,0x3f,sizeof mn); dfs(1,0); ans=f[1][0][0]; for(int j=0;j<n;j++)ans=min(ans,f[1][j][1]); printf("%d\n",ans); return 0; }