323. 战略游戏
题目链接
323. 战略游戏
鲍勃喜欢玩电脑游戏,特别是战略游戏,但有时他找不到解决问题的方法,这让他很伤心。
现在他有以下问题。
他必须保护一座中世纪城市,这条城市的道路构成了一棵树。
每个节点上的士兵可以观察到所有和这个点相连的边。
他必须在节点上放置最少数量的士兵,以便他们可以观察到所有的边。
你能帮助他吗?
例如,下面的树:
只需要放置 \(1\) 名士兵(在节点 \(1\) 处),就可观察到所有的边。
输入格式
输入包含多组测试数据,每组测试数据用以描述一棵树。
对于每组测试数据,第一行包含整数 \(N\),表示树的节点数目。
接下来 \(N\) 行,每行按如下方法描述一个节点。
节点编号:(子节点数目) 子节点 子节点 …
节点编号从 \(0\) 到 \(N−1\),每个节点的子节点数量均不超过 \(10\),每个边在输入数据中只出现一次。
输出格式
对于每组测试数据,输出一个占据一行的结果,表示最少需要的士兵数。
数据范围
\(0<N≤1500,\)
一个测试点所有 \(N\) 相加之和不超过 \(300650\)。
输入样例:
4
0:(1) 1
1:(2) 2 3
2:(0)
3:(0)
5
3:(3) 1 4 2
1:(1) 0
2:(0)
0:(0)
4:(0)
输出样例:
1
2
解题思路
树形dp
-
状态表示:
-
- \(f[u][0]\) 表示没有选择 \(u\) 节点这棵以 \(u\) 为根节点的子树放置士兵的最少数量
-
- \(f[u][1]\) 表示选择 \(u\) 节点这棵以 \(u\) 为根节点的子树放置士兵的最少数量
-
状态计算:
-
- \(f[u][0]=\sum f[v][1]\)
-
- \(f[u][1]=1+\sum min(f[v][0],f[v][1]\)
分析:如果当前节点没有选,则其子节点必须要选,否则连向子节点的这条边不合题意;如果当前节点选了,则子节点可选可不选,取较小的即可
- 时间复杂度:\(O(n)\)
代码
// Problem: 战略游戏
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/325/
// Memory Limit: 10 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1510;
int n,f[N][2];
bool v[N];
vector<int> adj[N];
void dp(int x)
{
f[x][0]=0,f[x][1]=1;
for(int y:adj[x])
{
dp(y);
f[x][0]+=f[y][1];
f[x][1]+=min(f[y][0],f[y][1]);
}
}
int main()
{
while(~scanf("%d",&n))
{
memset(v,0,sizeof v);
for(int i=0;i<n;i++)adj[i].clear();
int x,s,y;
for(int i=0;i<n;i++)
{
scanf("%d:(%d)",&x,&s);
while(s--)
{
scanf("%d",&y);
adj[x].pb(y);
v[y]=true;
}
}
int root=0;
while(v[root])root++;
dp(root);
printf("%d\n",min(f[root][0],f[root][1]));
}
return 0;
}