洛谷 P2016 分析(别看)
2026-07-07 15:40:02
发布于:河北
1 题目
1.1 题目所需求出内容
对于本题,最终要求:求一个最小值
1.2 题目背景、允许、禁止与限制
背景:有一个有 个结点的无根树
允许:放置尽可能少的士兵,在结点 放置士兵可以使所有与结点 相连的边被监视
禁止:
限制:最后要使整棵树的每条边都会被监视
1.3 题目数据范围与猜测
1.4 一句话概括题意
有一个无根树,可以放置一个士兵,在结点 放置士兵可以使所有与结点 相连的边被监视。最终求使整棵树的每条边都会被监视到的情况下需放置士兵的最小数量
2 题目破题推导
注:以下六种方法都可以考虑一下
2.1 大拆小、小组大
2.2 正向思维转逆向思维,逆向思维转正向思维
2.3 以终为始、以始为终
2.4 数学
2.5 分情况考虑
首先我们知道,一条边 只可能被点 或者点 监视
- 如果点 监视了,那么点 就随意(点 当然也可以监视,比如用于监视点 的子节点)
- 如果点 没有监视,那么点 只能监视
2.6 手动推导
2.7 边界测试
3 模型匹配
提取关键词:
树+动态影响,是树形dp
4 具体方案
主要就是dp数组的定义
将 设定为:以 为根的子树,在结点 放置()以及不放置()时所需的最小士兵数量
剩下的都比较简单
5 最终代码(禁止抄袭,仅用于参考)
#include <bits/stdc++.h>
using namespace std;
int n;
const int N = 6e3 + 10;
int a[N];
vector<int> g[N];
int dp[N][2];
bool has_father[N];
void dfs(int u, int fa){
dp[u][0] = 0, dp[u][1] = 1;
for (int now : g[u]){
if (now == fa){
continue;
}
dfs(now, u);
dp[u][0] += dp[now][1];
dp[u][1] += min(dp[now][1], dp[now][0]);
}
}
int main(){
memset(has_father, false, sizeof(has_father));
cin >> n;
for (int i = 1;i <= n;i++){
int l, k;
cin >> l >> k;
for (int j = 1;j <= k;j++){
int x;
cin >> x;
g[l].push_back(x);
g[x].push_back(l);
has_father[x] = true;
}
}
int root = 1;
for (int i = 0;i < n;i++){
if (has_father[i] == false){
root = i;
break;
}
}
dfs(root, -1);
cout << min(dp[root][0], dp[root][1]);
return 0;
}
这里空空如也
















有帮助,赞一个