给你一个图 在尽量少的节点上放灯 使得所有边所有边都被照亮 每盏灯照亮以他为端点的所有边
求在灯总数最小的前提下 被两盏灯照亮的边数尽量大(即被一条边照亮的边数尽量小)
另x = Ma + c (a是灯的数量,c是被两盏灯照亮边数)转换求x 最小
M是一个很大的数 x取最小时 x/m整数部分就是所求灯最小值 x%m就是被一条边照亮的边数
M比 c的最大理论值和a的最小理论值差 还要大的数 那么x都是a起到决定性作用 a相同时在取决于c
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 1010;
vector <int> adj[maxn];
int vis[maxn][2], dp[maxn][2], n, m;
int dfs(int i, int j, int f)
{
if(vis[i][j])
return dp[i][j];
vis[i][j] = 1;
int& ans = dp[i][j];
ans = 2000;
for(int k = 0; k < adj[i].size(); k++)
if(adj[i][k] != f)
ans += dfs(adj[i][k], 1, i);
if(!j && f >= 0)//不是根并且父节点没有放灯 那么被一盏灯照亮的边加1
ans++;
if(j || f < 0)//是根 或者 父节点已经放了灯才能不放
{
int sum = 0;
for(int k = 0; k < adj[i].size(); k++)
if(adj[i][k] != f)
sum += dfs(adj[i][k], 0 , i);
if(f >= 0)//不放的话父节点肯定已经放了 那么被一盏灯照亮的边加1
sum++;
ans = min(ans, sum);
}
return ans;
}
int main()
{
int T, u, v;
scanf("%d", &T);
while(T--)
{
scanf("%d %d", &n, &m);
for(int i = 0; i < n; i++)
adj[i].clear();
for(int i = 0; i < m; i++)
{
scanf("%d %d", &u, &v);
adj[u].push_back(v);
adj[v].push_back(u);
}
memset(vis, 0 ,sizeof(vis));
int ans = 0;
for(int i = 0; i < n; i++)
if(!vis[i][0])
ans += dfs(i, 0, -1);
printf("%d %d %d\n", ans/2000, m-ans%2000, ans%2000);
}
return 0;
}