把无向图转化成有根树。
两个优化的量v1,v2,把二者组合成一个量Mv1+v2,让M是一个比“v2的最大理论值和v2的最小理论值之差”还要大的数,
就可以先决定v1,再决定v2。
取M=2000,ans表示该节点放灯的情况,sum表示该节点不放灯的情况
#include<bits/stdc++.h>
using namespace std;
vector<int> adj[1010];
int vis[1010][2],d[1010][2],n,m; //d[i][j]表示i的父节点是否放灯的值为j,以i为根的树的最小值
int dp(int i,int j,int f){ //f是i的父节点
if(vis[i][j]) return d[i][j]
vis[i][j]=1;
int &ans=d[i][j];
ans=2000;
for(int k=0;k<adj[i].size();k++)
if(adj[i][k]!=f)
ans+=dp(adj[i][k],1,i);
if(!j && f>=0) ans++; //如果i不是根或者其父节点没放灯,则x加1
if(j || f<0){ //i是根或者其父节点已放灯,i才可以不放灯
int sum=0;
for(int k=0;k<adj[i].size();k++)
if(adj[i][k]!=f)
sum+=dp(adj[i][k],0,i);
if(f>=0) sum++;
ans=min(ans,sum);
}
return ans;
}
int main(){
int T,a,b;
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",&a,&b);
adj[a].push_back(b);
adj[b].push_back(a);
}
memset(vis,0,sizeof(vis));
int ans=0;
for(int i=0;i<n;i++)
if(!vis[i][0])
ans+=dp(i,0,-1);
printf("%d %d %d\n",ans/2000,m-ans%2000,ans%2000);
}
return 0;
}