UVA10859 Placing Lampposts 树形DP

这个题目类似之前做过,方案是一个节点放灯和不放灯会有不同的递推方法。树形DP入门题应该碰到过类似的题目

递推方程详见注释


Time Limit: 3000MS Memory Limit: Unknown 64bit IO Format: %lld & %llu

[]   [Go Back]   [Status]  

Description

Download as PDF

Input: Standard In
Output: Standard Out

Next Generation Contest 1 

Time Limit: 2 seconds

Problem D
Placing Lampposts

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;
}


### 回答1: 我是AI语言模型,我没有个人喜好,但是通常babel、eslint等配置文件会放在项目根目录下的一个特定文件夹中,例如babel配置文件会放在根目录下的.babelrc文件中,eslint配置文件会放在根目录下的.eslintrc文件中。这样做可以方便地管理和维护这些配置文件。 ### 回答2: 对于Babel、ESLint等配置文件,它们的位置并没有固定的规定。不过,对于一般的项目来说,最好将这些配置文件都放在项目根目录下,并且以文件名前缀“.”(如“.babelrc”、“.eslintrc”)的形式命名。 将这些配置文件都集中在项目根目录下可以方便管理和维护。由于这些文件都是与项目相关的配置,将它们放在根目录下可以让项目更加有序和清晰,而且也不容易被误删或遗漏。 同时,文件名前缀为“.”的命名方式也符合Unix/Linux系统中隐藏文件的命名规则,可以让这些配置文件在项目文件列表中不会过于突兀地出现。而且,在命令行中使用ls等命令查看文件列表时,也可以通过加上“-a”参数显示这些隐藏文件。 除了根目录,有些项目也可能会选择将这些配置文件放在各自的模块目录下。这种方式对于大型项目来说可能更有利于模块化管理。不过,这种方式也会增加对每个模块的配置文件进行管理的难度和复杂度。 综上所述,将Babel、ESLint等配置文件放在项目根目录下,并以文件名前缀“.”的方式命名是一个比较普遍的做法,也是一个比较方便和简单的管理方式。 ### 回答3: 对于babel和eslint等工具的配置文件,我倾向于将它们放在项目根目录下的特定文件夹中。 首先,将配置文件放在根目录下可以方便地找到它们,并且可以避免它们散落在项目的不同目录中,降低了管理的难度。例如,将babel配置文件放在根目录下的`.babelrc`文件中,eslint配置文件放在`.eslintrc`文件中等等。 其次,将这些配置文件作为代码的一部分,而不是分散在各个文件中。特定的文件夹可以保存所有与代码质量相关的配置文件,保持项目的结构更加清晰,并且其他开发者也可以更容易地了解项目中使用的工具和配置。 最后,将这些配置文件放在指定文件夹中也可以方便地进行版本控制。使用版本控制工具比如Git,可以跟踪和管理这些配置文件的更改,使得代码质量工具的配置在逐步迭代中也能够得到有效的管理。 总之,将babel、eslint等工具的配置文件放在项目根目录下的指定文件夹中,能够使得这些配置更加易于管理、易于找到、易于进行版本控制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值