皇宫看守-树形DP

Description

  太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。
  皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状;某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。
  可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。

  编程任务:帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。

Input

  输入文件中数据表示一棵树,描述如下:
  第1行 n,表示树中结点的数目。
  第2行至第n+1行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号i(0 < i<=n),在该宫殿安置侍卫所需的经费k,该边的儿子数m,接下来m个数,分别是这个节点的m个儿子的标号r1,r2,...,rm。
  对于一个n(0 < n <= 1500)个结点的树,结点标号在1到n之间,且标号不重复。

Output

  输出文件仅包含一个数,为所求的最少的经费。

Sample Input

6
1 30 3 2 3 4
2 16 2 5 6
3 5 0
4 4 0
5 11 0
6 5 0

Sample Output

25


思路

  • f[i][0/1/2]=以i为跟的子树全覆盖所需的最小花费,0表示子树的根被父亲看,1表示子树的根被儿子看,2表示子树的根被自己看
  • 看似一样,实则差异巨大的题目:https://www.cnblogs.com/wuwendongxi/p/13767484.html

代码

#include <iostream>
#include <cstdio>
#define maxn 2005
using namespace std;
int head[maxn],cnt,n,r[maxn],c[maxn],f[maxn][3];
struct fdfdfd{int next,to;}e[maxn];
void addedge(int x,int y){e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;}
void dp(int x,int pre)
{
	int temp=0x7fffffff;
	for(int i=head[x];i;i=e[i].next)
	{
		int v=e[i].to; dp(v,x);
		f[x][0]+=min(f[v][2],f[v][1]);
		f[x][2]+=min(f[v][2],min(f[v][1],f[v][0]));
		f[x][1]+=min(f[v][2],f[v][1]);
		temp=min(temp,f[v][2]-min(f[v][2],f[v][1]));
	}
	f[x][1]+=temp; f[x][2]+=c[x];
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	{
		int u,v,num; scanf("%d",&u); scanf("%d%d",&c[u],&num);
		for(int j=1;j<=num;++j) scanf("%d",&v),addedge(u,v),r[v]=1;
	}
	int root;
	for(int i=1;i<=n;++i)
		if(!r[i]) {root=i; break;}
	dp(root,0);
	printf("%d",min(f[root][1],f[root][2]));
	return 0;
}
原文地址:https://www.cnblogs.com/wuwendongxi/p/13767287.html