【BZOJ4711】小奇挖礦 樹形DP
阿新 • • 發佈:2017-10-09
其中 getc scrip namespace 描述 eof min 輸出 16px
輸出一行一個整數,表示最小費用。
2 5 9 11 15 19 20
1 4
1 3
1 7
4 6
2 8
2 3
3 5
【樣例解釋】
在1,2號星球建立倉庫。
【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<=100000Output
Sample Input
8 102 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號星球建立倉庫。
題解:前幾天做了難麽多奇葩的樹形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