题目大意:
Bob非常喜欢电脑游戏,特别是那种战略游戏,但有时他不能非常快速的找出最佳策略,因此他时常沮丧,现在他遇到了一个难题,在游戏中他要保卫一座中世纪古堡,通向古堡的道路是树形的(即一个结点可以把手与之相连的两条边),现在的问题是如何配置士兵使其可以把手通向古堡的所有道路并且士兵数量最少。
现有多个测例(测例数无上限),每个测例中都会给出结点数量n(0 < n ≤ 1,500),结点编号为[0, n-1],接着会给出每个结点所连接的边的数量,以及与之相连的结点(由于道路是树形的,因此输入中每条边只出现一次),现对于每个测例给出最少的把守士兵数量。
注释代码:
/*
* Problem ID : POJ 1463 Strategic game
* Author : Lirx.t.Una
* Language : C++
* Run Time : 641 ms
* Run Memory : 208 KB
*/
#include <string.h>
#include <stdio.h>
#define TRUE 1
#define FALSE 0
//最大结点数
#define MAXNODEN 1500
#define MIN(x,y) ( (x) < (y) ? (x) : (y) )
typedef char BOOL;
short fath[MAXNODEN];//father,记录每个结点的父亲结点的编号
short ncld[MAXNODEN];//number of children,记录每个结点子结点的数量
BOOL vist[MAXNODEN];//每个结点的dp值是否计算过
//dp[i][0]表示结点子树i的顶点(即i结点)不放置士兵后子树i最少把守士兵数量
//dp[i][1]表示结点子树i的顶点(即i结点)放置士兵后子树i最少把守士兵数量
//显然有dp[i][0] = ∑dp[c][1],c为i的子结点
//并且dp[i][1] = 1 + ∑MIN( dp[c][0], dp[c][1] )
short dp[MAXNODEN][2];
void
fdp( int node, int n ) {
if ( !ncld[node] ) {//如果node为叶子结点
//递归的终结
dp[node][0] = 0;
dp[node][1] = 1;
return ;
}
int c;//children,node的子结点
int dp0, dp1;//dp0表示node不放士兵的node子树最小士兵数
//dp1表示node放置士兵的最小数量
//初始化
dp0 = 0;
dp1 = 0;
for ( c = 0; c < n; c++ )
if ( fath[c] == node ) {//首先c必须是node的子结点
if ( !vist[c] ) {//并且c的dp值必须现计算过
//如果没计算过则需要递归计算一下
//这里采用记忆化所搜
//dp即打表值
fdp( c, n );
vist[c] = TRUE;
}
//运用上述的公式
dp0 += dp[c][1];
dp1 += MIN( dp[c][0], dp[c][1] );
}
//完成最后的打表
dp[node][0] = dp0;
dp[node][1] = dp1 + 1;
}
int
main() {
int n;//结点数
int f, c;//father、children,父结点和子结点
int nc;//number of chidren node,结点的子结点数
int root;//根结点
int i;//计数变量
while ( ~scanf("%d", &n) ) {
root = -1;//初始化
memset(vist, FALSE, sizeof(vist));
for ( i = 1; i <= n; i++ ) {
scanf("%d:(%d)", &f, &nc);
ncld[f] = nc;
if ( -1 == root )//第一个接受的父节点先暂时设置为根结点
root = f;
while ( nc-- ) {
scanf("%d", &c);
fath[c] = f;
//如果发现某个输入过程中
//当前子结点刚好和之前的root相等
//则需将当前父结点更新为根结点
if ( c == root )
root = f;
}
}
fath[root] = -1;//规定将根结点的父结点设为无效值
fdp( root, n );
printf("%d\n", MIN( dp[root][0], dp[root][1] ));
}
return 0;
}
无注释代码:
#include <string.h>
#include <stdio.h>
#define TRUE 1
#define FALSE 0
#define MAXNODEN 1500
#define MIN(x,y) ( (x) < (y) ? (x) : (y) )
typedef char BOOL;
short fath[MAXNODEN];
short ncld[MAXNODEN];
BOOL vist[MAXNODEN];
short dp[MAXNODEN][2];
void
fdp( int node, int n ) {
if ( !ncld[node] ) {
dp[node][0] = 0;
dp[node][1] = 1;
return ;
}
int c;
int dp0, dp1;
dp0 = 0;
dp1 = 0;
for ( c = 0; c < n; c++ )
if ( fath[c] == node ) {
if ( !vist[c] ) {
fdp( c, n );
vist[c] = TRUE;
}
dp0 += dp[c][1];
dp1 += MIN( dp[c][0], dp[c][1] );
}
dp[node][0] = dp0;
dp[node][1] = dp1 + 1;
}
int
main() {
int n;
int f, c;
int nc;
int root;
int i;
while ( ~scanf("%d", &n) ) {
root = -1;
memset(vist, FALSE, sizeof(vist));
for ( i = 1; i <= n; i++ ) {
scanf("%d:(%d)", &f, &nc);
ncld[f] = nc;
if ( -1 == root )
root = f;
while ( nc-- ) {
scanf("%d", &c);
fath[c] = f;
if ( c == root )
root = f;
}
}
fath[root] = -1;
fdp( root, n );
printf("%d\n", MIN( dp[root][0], dp[root][1] ));
}
return 0;
}
单词解释:
defend:vt, 防护,保卫
medieval:adj, 中世纪的