皇宫看守 LOJ10157

皇宫看守(程序文件名:guard.pas)

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

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

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

题解:

放置守卫使这棵树被看守 设

f\left ( x,0 \right )  为w点放置守卫 子树全部被看守的最优解

f\left ( x,1 \right )  为x别他的父亲看守可以x节点上无人的最优解,在这种状态下父节点必须放看守

f\left ( x,2 \right )  为x的子节点放置看守 x点无人

状态转移方程

f(x,0)=\sum min \left \{ f(y,0) ,f(y,1), f(y,2) \right \} + w[x]

f(x,1)=\sum min\left \{ f(y,0),f(y,2) \right \}

flag表示是否选取了f(y,0) flag=0表示没去,flag=1表示取

f\left ( x,2 \right ) 时必须选一个子节点,我们可以维护一个最小的f(y,0)-f(y,2)当flag=0时加上

f(x,2)=\begin{cases} & \text{ }\sum min\left \{ f(y,0),f(y,2) \right \} flag=1 \\ & \text{ }\sum min\left \{ f(y,0),f(y,2) \right \}+f(i,0)-f(i,2) flag=0 \end{cases}

 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=2200;
struct E{int x,y,next;}mm[MAXN<<1];
int w[MAXN],h[MAXN],len,f[MAXN][3],n;

inline void ins(int x,int y){
	++len;
	mm[len].x=x;mm[len].y=y;mm[len].next=h[x];h[x]=len;
}
void dfs(int x,int fa){
	int sum1=0,sum2=0,flag=0,fake=0x7fffffff;
	for(int k=h[x];k;k=mm[k].next){
		int y=mm[k].y;
		if(y==fa) continue;
		dfs(y,x);
		sum1+=min(f[y][0] , min(f[y][1],f[y][2]));
		if(f[y][0]<=f[y][2]){
			sum2+=f[y][0];
			flag=1;
		}else {
			sum2+=f[y][2];
			fake=min(fake,f[y][0]-f[y][2]);	
		}
	}
	f[x][0]=sum1+w[x];
	f[x][1]=sum2;
	f[x][2]=sum2;
	if(!flag) f[x][2]+=fake;
}
int main(){
	
	//freopen("guard.in","r",stdin);
	scanf("%d",&n);
	memset(h,0,sizeof h);len=0;//memset(f,127,sizeof f);
	for(int i=1,x,t,y;i<=n;i++){
		scanf("%d",&x);scanf("%d%d",&w[x],&t);
		for(int j=1;j<=t;j++){
			scanf("%d",&y);
			ins(x,y);ins(y,x);
		}
		if(t==0){
			f[x][1]=0;f[x][0]=f[x][2]=w[x];
		}
	}
	dfs(1,0);
	printf("%d\n",min(f[1][0],f[1][2]));
	return 0;
}

 

 

 

 

 

 

posted @ 2018-10-20 11:13  Exception2017  阅读(164)  评论(0编辑  收藏  举报