赞
踩
给出一棵包含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点放,子节点可放可不放
#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; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。