[洛谷 P2016] 战略游戏
题目链接
大致题意:
给出一棵包含n个节点的无根树,如果在i点放置一名守卫,那么这名守卫可以望到i点以及与i相连的边
求放置最少的守卫,使得可以望到所有的边
解题思路:
经典题型
本题是放置最少守卫可以望到所有边,换句话就是说一条边的两端节点必须选一个
对于一个节点来说,我们只有两种选择,放或者不放,且每名守卫只能望到与他相连的边
所以我们可以这样定义状态方程f[i][2] 表示在以i点为根节点不放守卫且满足条件的最少守卫或在以i点为根节点放守卫且满足条件的最少守卫
如果u点不放守卫,那么它的子节点必须放守卫
如果u点放守卫,那么它的子节点可放可不放
得出转移方程
f[u][0] += f[v][1]; //u点不放,子节点必须放
f[u][1] += min(f[v][1], f[v][0]);//u点放,子节点可放可不放
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1510;
int n, m;
vector<int>e[N];
int f[N][2]; //0表示以i点为根节点不放 1表示以i点为根节点放 的最少士兵
void dfs(int u, int fa) {
//初始化
f[u][0] = 0;f[u][1] = 1;
for (auto& v : e[u]) {
if (v == fa)continue;
dfs(v, u);
f[u][0] += f[v][1]; //u点不放,子节点必须放
f[u][1] += min(f[v][1], f[v][0]);//u点放,子节点可放可不放
}
}
int main(void)
{
cin >> n;
for (int i = 1; i <= n; ++i) {
int a, b, k; cin >> a >> k;
while (k--) {
cin >> b;
e[a].push_back(b);
e[b].push_back(a);
}
}
dfs(0, -1);
cout << min(f[0][0], f[0][1]) << endl;
return 0;
}