把無向圖轉化成有根樹。
兩個優化的量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;
}