这个题目类似之前做过,方案是一个节点放灯和不放灯会有不同的递推方法。树形DP入门题应该碰到过类似的题目
递推方程详见注释
Time Limit: 3000MS | Memory Limit: Unknown | 64bit IO Format: %lld & %llu |
Description

|
As a part of the mission �Beautification of Dhaka City�, the government has decided to replace all the old lampposts with new expensive ones. Since the new ones are quite expensive and the budget is not up to the requirement, the government has decided to buy the minimum number of lampposts required to light the whole city.
Dhaka city can be modeled as an undirected graph with no cycles, multi-edges or loops. There are several roads and junctions. A lamppost can only be placed on junctions. These lampposts can emit light in all the directions, and that means a lamppost that is placed in a junction will light all the roads leading away from it.
The �Dhaka City Corporation� has given you the road map of Dhaka city. You are hired to find the minimum number of lampposts that will be required to light the whole city. These lampposts can then be placed on the required junctions to provide the service. There could be many combinations of placing these lampposts that will cover all the roads. In that case, you have to place them in such a way that the number of roads receiving light from two lampposts is maximized.
Input
There will be several cases in the input file. The first line of input will contain an integer T(T<=30) that will determine the number of test cases. Each case will start with two integers N(N<=1000) and M( M<N) that will indicate the number of junctions and roads respectively. The junctions are numbered from 0 to N-1. Each of the next M lines will contain two integersa and b, which implies there is a road from junction a to b,
( 0<= a,b < N ) and a != b. There is a blank line separating two consecutive input sets.
Output
For each line of input, there will be one line of output. Each output line will contain 3 integers, with one space separating two consecutive numbers. The first of these integers will indicate the minimum number of lampposts required to light the whole city. The second integer will be the number of roads that are receiving lights from two lampposts and the third integer will be the number of roads that are receiving light from only one lamppost.
Sample Input
2 4 3 0 1 1 2 2 3 5 4 0 1 0 2 0 3 0 4 |
Sample Output
2 1 2 1 0 4 |
Problem Setter: Sohel Hafiz.
Special thanks to Per Austrin.
#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<cstdio>
using namespace std;
#define MAXN 1100
vector<int> g[MAXN];
#define add(a,b) g[a].push_back(b)
int f[MAXN][2][2],vis[MAXN];
int n,m;
/********************************************************************************
树形DP 求解用最少的灯照亮所有边的,并使同时被两盏灯照亮的边最数量最大
f(i,0,0) 以i为root的子树的边全部照亮所需的最少灯数,其中i没放灯
f(i,1,0) 以i为root的子树的边全部照亮所需的最少灯数,其中i放了灯
f(i,0,1) 以i为root的子树的边用最少的灯照亮后,被两个灯同时照亮的边最大数i没放灯
f(i,1,1) 以i为root的子树的边用最少的灯照亮后,被两个灯同时照亮的边最大数i放了灯
f(rt,0,0) = sum(f(i,1,0)) i是以rt为root的子树的孩子节点
f(rt,1,0) = sum(min(f(i,1,0),f(1,0,0)))+1
f(rt,0,1) = sum(f(i,1,1))
f(rt,1,1)=f_new(rt,1,1)
其中f_new(rt,1,1) 计算f(rt,1,0) 时相应方案累加值 加上被两盏灯照亮的边数
********************************************************************************/
void dfs(int u){
vis[u]=1;
f[u][0][0]=0;
f[u][1][0]=1;
f[u][0][1]=0;
f[u][1][1]=0;
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
if(vis[v]) continue;
dfs(v);
f[u][0][0]+=f[v][1][0];
f[u][0][1]+=f[v][1][1];
if(f[v][1][0]>f[v][0][0]){
f[u][1][0]+=f[v][0][0];
f[u][1][1]+=f[v][0][1];
}else if(f[v][1][0]<f[v][0][0]){
f[u][1][0]+=f[v][1][0];
f[u][1][1]+=f[v][1][1]+1;
}else if(f[v][1][1]<f[v][0][1]){ //当照亮所有边的灯数一样时不要想当然的选儿子节点被照亮的边
f[u][1][0]+=f[v][0][0];
f[u][1][1]+=f[v][0][1];
}else{
f[u][1][0]+=f[v][1][0];
f[u][1][1]+=f[v][1][1]+1;
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) g[i].clear();
for(int i=0;i<m;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
memset(vis,0,sizeof(vis));
int ans1=0,ans2=0;
for(int i=0;i<n;i++){
if(!vis[i]){
dfs(i);
if(f[i][0][0]<f[i][1][0])
ans1+=f[i][0][0],ans2+=f[i][0][1];
else if(f[i][0][0]>f[i][1][0])
ans1+=f[i][1][0],ans2+=f[i][1][1];
else if(f[i][0][1]>f[i][1][1])
ans1+=f[i][0][0],ans2+=f[i][0][1];
else
ans1+=f[i][1][0],ans2+=f[i][1][1];
}
}
printf("%d %d %d\n",ans1,ans2,m-ans2);
}
return 0;
}