皇宫看守(guard)

【问题描述】

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

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

【输入格式】 

输入文件中数据表示一棵树,描述如下: 

第1行 n,表示树中结点的数目。 

第2行至第n+1行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号i(0<I<=N),在该宫殿安置侍卫所需的经费K,该点的儿子数M,接下来M个数,分别是这个节点的M个儿子的标号R1,R2,...,RM。对于一个n(0 < n<=1500)个结点的树,结点标号在1到n之间,且标号不重复。 

【输出格式】

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

【输入样例】

6

1 30 3 2 3 4

2 16 2 5 6

3 5 0

4 4 0

5 11 0

6 5 0

【输出样例】

25

【解析】

这道题也是一道树形状态的动态规划啦.. #我在用肺说话..

这道题需要我们去定每一个点的状态.. 也就是关于该点是否有人和是否安全..

f[i][0]表示i点 没人 安全 所需的最小花费

f[i][1]表示i点 没人 不安全 所需的最小花费

f[i][2]表示i点 有人 安全 所需的最小花费

f[i][3]表示i点 有人 不安全  所需的的最小花费.. #第四种就不说啦.. 既然有人怎么会不安全呢.. 那么就可以不定义这种状态..

这个时候也许你就会问了.. f[i][1]有什么用.. 没人不安全的最小花费不就是0吗

这你就错了.. 我定义这些最小花费的前提是当前节点i为根的数中所有孩子节点都是安全情况下的..

那么问题来了 = =

没人并且安全这种状态怎么得到0.0.

如果想让一个节点没人安全,孩子节点中必须要有一个是有人的,但有人的孩子节点又可以有很多..

问题就出在这里.. 这个问题想了我挺久..

<pre name="code" class="cpp">void Tree_DP ( int x ){
	int i, j, k, y, f0, f1, f2;
	f0 = f1 = 0; f2 = w[x];
	int minn = 99999; bool bk = false;
	for ( k = first[x]; k; k = a[k].next ){
		y = a[k].y;
		Tree_DP (y);
		minn = _min ( minn, f[y][2] - f[y][0] );
		if ( f[y][2] > f[y][0] ) f0 += f[y][0];
		else {
			bk = true;
			f0 += f[y][2];
		}
		f1 += f[y][0];
		f2 += _min ( f[y][0], _min ( f[y][1], f[y][2] ) );
	}
	if ( bk == false ) f0 += minn;
	f[x][0] = f0;
	f[x][1] = f1;
	f[x][2] = f2;
}

 注意看以上代码关于f0、minn的部分.. 

我在孩子节点中间找一个从没人安全有人安全的最小增加的花费(在这里我默认所有孩子节点有人安全都比没人安全花费多)

那么假如有一个孩子节点的有人安全没人安全的花费少,那么不就解决了需要有人的这个问题了吗..就加上孩子节点有人安全的花费并标记bk已经找到了有人花费少的节点

最后如果还是没有,那就加上minn..

详细的就自己看吧..

【完整代码】

#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
struct node {
	int x, y, next;
}a[2100]; int first[2100], len, n;
void ins ( int x, int y ){
	len ++;
	a[len].x = x; a[len].y = y;
	a[len].next = first[x]; first[x] = len;
}
int f[2100][3], w[2100];
int _min ( int x, int y ){ return x < y ? x : y; }
void Tree_DP ( int x ){
	int i, j, k, y, f0, f1, f2;
	f0 = f1 = 0; f2 = w[x];
	int minn = 99999; bool bk = false;
	for ( k = first[x]; k; k = a[k].next ){
		y = a[k].y;
		Tree_DP (y);
		minn = _min ( minn, f[y][2] - f[y][0] );
		if ( f[y][2] > f[y][0] ) f0 += f[y][0];
		else {
			bk = true;
			f0 += f[y][2];
		}
		f1 += f[y][0];
		f2 += _min ( f[y][0], _min ( f[y][1], f[y][2] ) );
	}
	if ( bk == false ) f0 += minn;
	f[x][0] = f0;
	f[x][1] = f1;
	f[x][2] = f2;
}
bool v[2100];
int main (){
	int i, j, k, x, y;
	scanf ( "%d", &n );
	len = 0; memset ( first, 0, sizeof (first) );
	memset ( v, false, sizeof (v) );
	for ( i = 1; i <= n; i ++ ){
		scanf ( "%d", &x );
		scanf ( "%d%d", &w[x], &k );
		for ( j = 1; j <= k; j ++ ){
			scanf ( "%d", &y );
			ins ( x, y );
			v[y] = true;
		}
	}
	int st;
	for ( i = 1; i <= n; i ++ ){
		if ( v[i] == false ){ st = i; break; }
	}
	Tree_DP (st);
	printf ( "%d\n", _min ( f[st][0], f[st][2] ) );
	return 0;
}


  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值