题意:有一个n个点m条边的无向无环图,在尽量少的结点上放灯,使得所有边都被照亮,每栈灯将照亮以它为一个端点的所有边,在灯的总数最小的情况下,被两栈灯同时照亮的情况下边数最大
解法:树形DP(边覆盖)
d[i][0]=0,d[i][1]=1;
d[i][0]=d[j][1];
d[i][1]=min(d[i][0],d[i][1])
cnt[i][0],cnt[i][1]表示节点i放灯和不放灯的情况下边被两栈灯照亮的数量。
// File Name: 10859.cpp // Author: zlbing // Created Time: 2013/1/27 16:39:01 #include<iostream> #include<string> #include<algorithm> #include<cstdlib> #include<cstdio> #include<set> #include<map> #include<vector> #include<cstring> #include<stack> using namespace std; #define MAXN 1000+5 vector<int> adj[MAXN]; int n,m; int d[MAXN][2],cnt[MAXN][2]; bool vis[MAXN]; void dfs(int u) { vis[u]=true; d[u][0]=0,d[u][1]=1;cnt[u][0]=cnt[u][1]=0; for(int i=0;i<adj[u].size();i++) { int v=adj[u][i]; if(!vis[v]) { dfs(v); d[u][0]+=d[v][1]; cnt[u][0]+=cnt[v][1]; if(d[v][1]==d[v][0]) { d[u][1]+=d[v][1]; cnt[u][1]+=max(cnt[v][1]+1,cnt[v][0]); } else if(d[v][1]<d[v][0]) { d[u][1]+=d[v][1]; cnt[u][1]+=cnt[v][1]+1; } else { d[u][1]+=d[v][0]; cnt[u][1]+=cnt[v][0]; } } } } int main(){ int N; scanf("%d",&N); while(N--) { scanf("%d%d",&n,&m); int a,b; 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,ans1=0; for(int i=0;i<n;i++) if(!vis[i]){ dfs(i); if(d[i][0]<d[i][1]) { ans+=d[i][0]; ans1+=cnt[i][0]; } else if(d[i][0]==d[i][1]) { ans+=d[i][0]; ans1+=max(cnt[i][0],cnt[i][1]); } else{ ans+=d[i][1]; ans1+=cnt[i][1]; } } printf("%d %d %d\n",ans,ans1,m-ans1); } return 0; }