1. 程式人生 > >【BZOJ4711】小奇挖礦 樹形DP

【BZOJ4711】小奇挖礦 樹形DP

其中 getc scrip namespace 描述 eof min 輸出 16px

【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

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