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

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的主要内容,如果未能解决你的问题,请参考以下文章

bzoj4711-小奇挖矿

4711: 小奇挖矿

NOIP模拟赛小奇挖矿 2

8.24考试总结

2019.10.16&17小结

BZOJ_1060_时态同步_树形DP