题目
思路
一开始想到的是计算每个节点的儿子数量,然后贪心一边, 但是这样是错误的,下面给出例子证明贪心法是错误的。
自顶向下错误证明:
自底向上错误证明:
说明:由于贪心策略,我们把儿子多的节点先给挂了, 导致我们最后的节点数为200(这里上面的单个节点忽略),但是正解为100。
所以这道题我们应该暴力求解,我们先记录一下每一个结点的儿子数量, 然后将每一层的边记录,然后按层枚举每一条边,这样的做法上界为\(\Theta(2^n)\) , 其中\(n\)为边的数量。
这里学习一下反向边的判断, 由于加入无向图加边的时候都是成对加入的, 所以相邻的两个\(idx\)一定满足关系\(\oplus = 1\), 所以我们可以用该条件判断两个数是否为反向边。
Code
#include <iostream>
#include <vector>
#include <cstring>
#define sz(x) (int)x.size()
using i64 = long long;
const int N = 500, M = 2 * N ;
int h[N], e[M], ne[M], idx;
std::vector<int> level[N];
int cnt[N], c[M];
int n, m, ans;
void add(int a, int b) {
ne[idx] = h[a], h[a] = idx, e[idx ++] = b;
ne[idx] = h[b], h[b] = idx, e[idx ++] = a;
}
int dfs_level(int u, int depth, int father) {
cnt[u] = 1;
for(int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if(j == father) continue;
cnt[u] += dfs_level(j, depth + 1, u);
level[depth].push_back(i);
}
return cnt[u];
}
void dfs_draw(int j, int color) {
c[j] = color;
for(int i = h[e[j]]; ~i; i = ne[i]) {
if(i != (j ^ 1))
dfs_draw(i, color);
}
}
void dfs(int u, int s) {
ans = std::min(ans, s);
for(int i = 0; i < sz(level[u]); i ++) {
int j = level[u][i];
if(!c[j]) {
dfs_draw(j, 1);
dfs(u + 1, s - cnt[e[j]]);
dfs_draw(j, 0);
}
}
}
int main() {
std::cin >> n >> m;
memset(h, -1, sizeof h);
ans = n;
for(int i = 0; i < m; i ++) {
int a, b;
std::cin >> a >> b;
add(a, b);
}
dfs_level(1, 0, -1);
dfs(0, n);
std::cout << ans;
}