【问题描述】
Bob喜欢玩电脑游戏,特别是战略游戏。但是他经常无法找到快速玩过游戏的办法。现在他有个问题。他要建立一个古城堡,城堡中的路形成一棵树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到。请你编一程序,给定一树,帮Bob计算出他需要放置最少的士兵.
【输入格式】
测试数据表示一棵树,描述如下:
第一行 N,表示树中结点的数目。
第二行至第N+1行,每行描述每个结点信息,依次为:该结点标号i,k(后面有k条边与结点I相连)。接下来k个数,分别是每条边的另一个结点标号r1,r2,...,rk。
对于一个n(0<n<=1500)个结点的树,结点标号在0到n-1之间,在输入数据中每条边只出现一次。
【输出格式】
输出文件仅包含一个数,为所求的最少的士兵数目。
例如,对于如下图所示的树:
0
|
1
/ \
2 3
答案为1(只要一个士兵在结点1上)。
【输入样例1】
4
0 1 1
1 2 2 3
2 0
3 0
【输出样例1】1
【输入样例2】
5
3 3 1 4 2
1 1 0
2 0
0 0
4 0
【输出样例2】
2
【解析】
这道题和上一道【皇宫看守(guard)】有相似之处..但是这道题的状态定义与上一题有少许不同..
上一道题需要点安全,但这一道题是需要边安全 = =
这是个值得困扰的问题.. 你要边安全但是又只能搜索点0.0
那么我们就可以这样定义每一个点所代表的
比如说点i,我们就可以说点i是否有人,点i所连接的边(以i为根的树,同下)是否安全。这样来做一个状态,我们又可以像上一题那样分出4种状态。
f[i][0] 表示点i 没人 所连接的边 安全 的最小花费
f[i][1] 表示点i 没人 所连接的边 不安全 的最小花费
f[i][2] 表示点i 有人 所连接的边 安全 的最小花费
f[i][3] 表示点i 有人 所连接的边 不安全 的最小花费 #既然有人怎么会不安全呢.. 所以这种状态可以不管0.0
这个时候问题也就来了.. 没人不安全这个状态有没有用呢..
上一题是看点是否安全的,也就是说,以下这种状态是成立的(1表示有人,0表示没人):
1——0——0——1(根)
这样子四个点都是安全的。
但现在需要的是边安全,那么在同样情况下:
1——0——0——1(根)
这是不安全的,因为红色的边这一条边是不安全的,而蓝色这一个点的状态正是 没人不安全..
以上证明:没人不安全 这个状态是没有用的!
那么同样的道理,没人安全这种状态就需要孩子节点全都是有人安全的!
那这道题就轻易解决啦!
【核心代码】
void Tree_DP ( int x ){
int i, j, k, f0, f1;
f0 = 0; f1 = 1;
for ( k = first[x]; k; k = a[k].next ){
int y = a[k].y;
Tree_DP (y);
f0 += f[y][1];
f1 += _min ( f[y][1], f[y][0] );
}
f[x][0] = f0;
f[x][1] = f1;
}【完整代码】
#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][2];
int _min ( int x, int y ){ return x < y ? x : y; }
void Tree_DP ( int x ){
int i, j, k, f0, f1;
f0 = 0; f1 = 1;
for ( k = first[x]; k; k = a[k].next ){
int y = a[k].y;
Tree_DP (y);
f0 += f[y][1];
f1 += _min ( f[y][1], f[y][0] );
}
f[x][0] = f0;
f[x][1] = f1;
}
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%d", &x, &k );
for ( j = 1; j <= k; j ++ ){
scanf ( "%d", &y );
ins ( x, y );
v[y] = true;
}
}
int st;
for ( i = 0; i < n; i ++ ){
if ( v[i] == false ){
st = i;
break;
}
}
Tree_DP (st);
printf ( "%d\n", _min ( f[st][0], f[st][1] ) );
return 0;
}
针对一个以树状结构表示的古城堡道路布局,探讨如何在关键节点部署最少数量的士兵以确保所有道路的安全监视。通过定义不同的状态并采用动态规划的方法,解决了此问题。
1019

被折叠的 条评论
为什么被折叠?



