题意:
有n个点m条边的无向无环图,在尽量少的点放灯,让所有边都能被照,且尽量多的边同时被找到。
思路:
题目有两个地方要控制,a:一个是放灯的点——要尽量少,b:第二个是被两盏灯照到的边—-尽量多。
学到一个小技巧,就是将第二问题转为b:被一盏灯照到的边 — 尽量少,所求即x=Ma+b最小,M为一个比a,b大得多的数。
然后就是控制x使其最小了。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1005;
const int M = 2000;
int vis[N], dp[N][2];
vector <int> ege[N];
int n, m;
void dfs(int u) {
vis[u] = 1;
dp[u][0] = 0;//u点不放灯
dp[u][1] = M;//u点放灯
for (int i = 0; i < ege[u].size(); i++) {
int v = ege[u][i];
if (vis[v])
continue;
dfs(v);
dp[u][0] += dp[v][1] + 1;
if (dp[v][0] < dp[v][1])
dp[u][1] += dp[v][0] + 1;
else
dp[u][1] += dp[v][1];
}
}
int main() {
int cas;
scanf("%d", &cas);
while (cas--){
memset(dp, 0, sizeof(dp));
memset(vis, 0, sizeof(vis));
for (int i = 0; i < n; i++)
ege[i].clear();
scanf("%d%d", &n, &m);
int u, v;
for (int i = 0; i < m; i++) {
scanf("%d%d", &u, &v);
ege[u].push_back(v);
ege[v].push_back(u);
}
int ans = 0;
for (int i = 0; i < n; i++) {
dfs(i);
ans += min(dp[i][0], dp[i][1]);
}
printf("%d %d %d\n", ans / M, m - (ans%M), ans%M);
}
return 0;
}