uva 10859 - Placing Lampposts(樹形dp
###兩個別人家的代碼,沒有註釋看了很久
###所以自己改寫了一遍,附註釋
https://blog.csdn.net/keshuai19940722/article/details/19363649
https://blog.csdn.net/deepquiet/article/details/50609020
//:page70(劉汝佳算法競賽入門經典--訓練指南)
//樹形遞歸
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<memory.h>
using namespace std;
const int M = 2000;//每開一個燈就加M
const int N = 1010;
int n,m;//點數和m行數據
int v[N];
int dp[N][2];//dp[i][j]以i爲根節點的狀態是j的最小X j=0 or 1(on/off)
vector<int> g[N];//鄰接矩陣
void init() //數據初始化
{
scanf("%d%d",&n,&m);
for(int i = 0;i < n;i++) g[i].clear(); //清空數據,每次重新輸入後
memset(v,0,sizeof(v));
memset(dp,0,sizeof(dp));
for(int i = 0;i < m;i++)
{
int a,b;
scanf("%d%d",&a,&b);//無向圖 初始化鄰接表
g[a].push_back(b);
g[b].push_back(a);
}
}
void dfs(int x)//深搜遍歷整棵樹 以x爲頂點 0<x<n-1
{
//根節點預處理
v[x] = 1; //表明這個點已經遍歷過
dp[x][0] = 0;//頂點不開燈爲0
dp[x][1] = M;//開燈爲M
//子節點
for(int i = 0;i < g[x].size();i++)//遍歷鄰接表
{
int u =g[x][i];//與X相鄰的點
if(v[u]) continue; //防止重複遍歷
dfs(u);//深搜繼續遍歷下一個
dp[x][0] += dp[u][1] + 1;//父節點不放燈的情況,子節點放燈
if(dp[u][1] > dp[u][0]) dp[x][1] += dp[u][0]+1;//父節點放燈,並且子節點不放燈
else dp[x][1] += dp[u][1];//兩個都放燈
}
}
void solve()
{
int ans = 0;
for(int i = 0;i < n;i++)//遍歷節點
{
if(!v[i])//如果是沒有遍歷過的點,因爲每次DFS都會把其子點遍歷,這些點就沒有必要了
{
dfs(i);
ans += min(dp[i][0],dp[i][1]);//選擇較小的
}
}
printf("%d %d %d\n",ans/M,m-ans%M,ans%M);
}
int main()
{
int T; scanf("%d",&T);
while(T--)
{
init();
solve();
}
return 0;
}