题目大意
对于任意一条边,其都只有两种被覆盖的可能:
1.被上面的节点(父节点)覆盖
2.被下面的节点(子节点)覆盖
故容易推出状态表示和方程
dp[i][j]:以i为根的子树的所有边被覆盖且i的状态为j的所有方案的数量最小值
(j = 0表示i不放士兵,j = 0为i放士兵)
转移方程
dp[i][0] = ∑(dp[son][1])
dp[i][1] = ∑(min(dp[son][1],dp[son][0]) + 1;
边界设置:对于叶子节点 dp[i][0] = 0, dp[i][1] = 1; 从定义出发
AC代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
#define IO std::ios::sync_with_stdio(false); cin.tie(0)
#define ll long long
#define ull unsigned long long
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(), (x).end()
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define per(i, l, r) for (int i = l; i >= r; --i)
#define mset(s, _) memset(s, _, sizeof(s))
#define mcpy(s, _) memcpy(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define vi vector<int>
#define vpii vector<pii>
#define mp(a, b) make_pair(a, b)
#define pll pair <ll, ll>
#define fir first
#define sec second
#define inf 0x3f3f3f3f
inline int lowbit(int x) {return x & -x;}
template< typename T > inline void get_min(T &x, T y) {if(y < x) x = y;}
template< typename T > inline void get_max(T &x, T y) {if(x < y) x = y;}
inline int read() {
int x = 0, f = 0; char ch = getchar();
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) x = 10 * x + ch - '0', ch = getchar();
return f ? -x : x;
}
template<typename t> inline void print(T x) {
if (x < 0) putchar('-'), x = -x;
if (x >= 10) print(x / 10);
putchar(x % 10 + '0');
}
template<typename t> inline void print(T x, char let) {
print(x), putchar(let);
}
const int N = 4e3 + 10, mod = 1e9 + 7;
int n, m;
int h[N], nex[N], v[N], idx;
void add(int a, int b) {
v[idx] = b; nex[idx] = h[a]; h[a] = idx ++ ;
}
int f[N][5], st[N];
void init() {
mset(h, -1); mset(f, 0); mset(st, 0); idx = 0;
}
void dp(int u) {
bool fg = 0;
for(int i = h[u]; ~i; i = nex[i]) {
int j = v[i];
fg = 1;
dp(j);
f[u][0] += f[j][1];
f[u][1] += min(f[j][0], f[j][1]);
}
f[u][1] += 1;
if(!fg) {
f[u][0] = 0; f[u][1] = 1;
}
}
int main() {
while(cin >> n) {
init();
rep(i, 1, n) {
int a, num, b; char t;
cin >> a >> t >> t >> num >> t;
rep(j, 1, num) {
cin >> b; add(a, b); st[b] = 1;
}
}
int root = 0;
while(st[root]) root ++ ;
dp(root);
cout << min(f[root][1], f[root][0]) << endl;
}
return 0;
}Cell Phone Network (https://ac.nowcoder.com/acm/problem/24953)
与 Strategic game的对比
树的最小支配集问题 与 树的最小点覆盖的对比
附上雨巨的画图解释
研究对象不同导致状态转移的不同,前者研究的是覆盖住所有的点,树的任意一个点都可能被自身,儿子,父亲三者之一所覆盖,故状态转移有三种可能,后者研究的是覆盖所有的边,任意的边只可能连接两个节点,故状态转移只能来自二者之一</ll,></int,>

京公网安备 11010502036488号