uva10859 - Placing Lampposts 无根树转有根树 DP

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 integers a 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

  有N个点M条边,给出无向无环图,在点上放灯可以照亮这个点连接的边,要让所有边都被照亮的情况下用的灯最少。并且在灯最少的情况下一条边同时被两个端点照亮的边尽量多。

  无向无环图就相当于多棵树,分别算出每棵树的答案加起来。书上讲的这个方法真是好啊。。

  如果有两个量v1,v2,要先满足v1最小,在v1最小的情况下v2最小,就可以设一个M,设x=Mv1+v2。M必须比v2理论上的最大值和最小值之差大。如果v2最大值也不大的话,设M大于v2最大值,那么x=Mv1+v2首先受v1的影响,v1一旦确定,v2的值怎么也不会影响,那么取最小的x,x/M,x%M就是v1,v2的最优值。

  这道题取M为2000,v1为放灯个数,v2为只被一个端点照亮的边的个数。可以在建有根树的过程中完成DP。对每棵树任取一个点为根节点(不影响答案)。如果是根节点或者它的父节点放了,这个点就可以选择放或不放,否则也就是既不是根结点而且父节点也没放,那么这个点一定要放。判断一条边被几个点照亮的时候在那条边的第二个点放不放的时候再确定(也就是子结点)。

  DP(i,j,f)为以i结点为根,它父节点为f,j=1是父节点放灯,j=0是父结点不放灯,这样的树的Mv1+v2的值。保存状态的时候不用保存f,f只是用来找子结点的时候避免找到父结点。设根结点的父节点为-1。


#include<cstdio>
#include<algorithm>
#include<iostream>
#include<sstream>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define INF 0x3f3f3f3f
#define MAXN 1010
#define MAXM 1010
#define eps 1e-9
#define pi 4*atan(1.0)
#define pii pair<int,int>
using namespace std;
int T,N,M,vis[MAXN][2],d[MAXN][2];
vector<int> v[MAXN];
int DP(int i,int j,int f){
    int &ans=d[i][j];
    if(vis[i][j]) return ans;
    vis[i][j]=1;
    ans=2000;
    for(int k=0;k<v[i].size();k++){
        int son=v[i][k];
        if(son!=f) ans+=DP(son,1,i);
    }
    if(!j&&f!=-1) ans++;
    if(f==-1||j){
        int s=0;
        for(int k=0;k<v[i].size();k++){
            int son=v[i][k];
            if(son!=f) s+=DP(son,0,i);
        }
        if(f!=-1) s++;
        ans=min(ans,s);
    }
    return ans;
}
int main(){
    freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        int ans=0;
        scanf("%d%d",&N,&M);
        for(int i=0;i<N;i++) v[i].clear();
        for(int i=0;i<M;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            v[a].push_back(b);
            v[b].push_back(a);
        }
        memset(vis,0,sizeof(vis));
        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;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值