P1453 城市环路

题目背景

一座城市,往往会被人们划分为几个区域,例如住宅区、商业区、工业区等等。B市就被分为了以下的两个区域——城市中心和城市郊区。在着这两个区域的中间是一条围绕B市的环路,环路之内便是B市中心。

题目描述

整个城市可以看做一个N个点,N条边的单圈图(保证图连通),唯一的环便是绕城的环路。保证环上任意两点有且只有2条路径互通。图中的其它部分皆隶属城市郊区。

现在,有一位名叫Jim的同学想在B市开店,但是任意一条边的2个点不能同时开店,每个点都有一定的人流量Pi,在该点开店的利润就等于该店的人流量Pi×K(K≤10000),K的值将给出。

Jim想尽量多的赚取利润,请问他应该在哪些地方开店?

输入输出格式

输入格式:

第一行一个整数N 代表城市中点的个数。城市中的N个点由0~N-1编号。

第二行N个正整数,表示每个点的人流量Pi(Pi≤10000)。

下面N行,每行2个整数A,B,表示A,B建有一条双向路。

最后一行一个实数K。

输出格式:

一个实数M,(保留1位小数),代表开店的最大利润。

输入输出样例

输入样例#1: 复制
4
1 2 1 5
0 1
0 2
1 2
1 3
2
输出样例#1: 复制
12.0

说明

【数据范围】

对于20%的数据,N≤100.

对于另外20%的数据,环上的点不超过2000个

对于50%的数据 N≤50000.

对于100%的数据 N≤100000.

//一道上午的时候不知道哪错了发了讨论然后放弃了的题。
//感谢@sqairy 大佬   一语道破我的错误所在
 
//楼下都用的dfs找环,当时没想出用dfs做来,所以在输入的时候处理的环 
//因为如果一条边的端点已经全部出现的话,我们把这条边加进去,就构成了一个环,
//所以开一个flag[]标记,标记这个点有没有输入过,
//如果 flag[u]&&flag[v],那么这肯定是一个环上的一条边,然后让A=u,B=v,
//并且我们不把这条边加进图里,这样就构成了一棵树 
//从A,B做两次树形DP,找最大值。 和没有上司的舞会那道题一样 
//还有就是要取max(dp[A][0],dp[B][0]),
//为什么是不选A和B的情况的最大值:
//因为我们如果是在选A或B的情况中取最大值,我们不能保证A(B)选了,但是B(A)没选

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;

const int N=1e6+5;

int n,A,B;
double K;
int p[N];
int head[N],num_edge;
bool flag[N];
double dp[N][2];
struct Edge
{
    int v,nxt;
}edge[N<<1];

int read()
{
    char c=getchar();int num=0;
    for(;!isdigit(c);c=getchar());
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num;
}

void add_edge(int u,int v)
{
    edge[++num_edge].v=v;
    edge[num_edge].nxt=head[u];
    head[u]=num_edge;
}

void dfs(int u,int fa)
{
    dp[u][1]=p[u],dp[u][0]=0;
    for(int i=head[u],v;i;i=edge[i].nxt)
    {
        v=edge[i].v;
        if(v==fa)
            continue;
        dfs(v,u);
        dp[u][0]+=max(dp[v][0],dp[v][1]);    //这个点不选,加上它的儿子选或不选的最大值 
        dp[u][1]+=dp[v][0];    //这个点选,它的儿子们不能选 
    }
}

int main()
{ 
    n=read();
    for(int i=0;i<n;++i)
        p[i]=read();
    bool ok=0;    //标记找没找到环 
    for(int i=1,u,v;i<=n;++i)
    {
        u=read(),v=read();
        if(!ok&&flag[u]&&flag[v])    //一条边的两个端点都出现过,那么着肯定是一个环上的边 
        {
            ok=1;    //找到环了 
            A=u,B=v;    //记录两个端点 
            continue;    //不加这条边 
        }
        flag[u]=flag[v]=1;        //标记两个端点已经出现过 
        add_edge(u,v);    //加边 
        add_edge(v,u);
    }
    scanf("%lf",&K);
    dfs(A,A);    //从A跑DFS 
    double ans=dp[A][0];
    dfs(B,B);    //从B跑DFS 
    ans=max(ans,dp[B][0]);
    printf("%.1lf",ans*K);
    return 0;
}
原文地址:https://www.cnblogs.com/lovewhy/p/8477000.html