luogu P1453 城市环路

题目描述

整个城市可以看做一个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位小数),代表开店的最大利润。


很明显是基环树的题。

首先最后的K是没有什么用的,因为我们的运算里只有加法,所以把K放到最后乘也不影响。我们先不把K算进去。

先考虑一棵树的情况。设dp(u,0/1)表示u的子树可以得到的最大利润,并且u是选(1)还是不选(0)。那么递推公式显然:

[dp[u][1]=sum_{v{in}son[u]} dp[v][0];\ dp[u][0]=sum_{v{in}son[u]} Max(dp[v][0],dp[v][1]); ]

初始化dp(u,0)=0,dp(u,1)=P(u)。目标状态为Max(dp(root,0/1))。

然而基环树就不那么简单。

朴素的做法就是枚举环上的每条边,把它断掉之后原图会变成一棵树,然后在树上做刚才的dp,得出对应的ans。枚举完所有边后去最大值即可。

但是我们发现断开一条边以后对于每个点选与不选没有影响。但是可以消除后效性。所以我们只需要断开任意一条边,然后分别以这条边的两个端点为根dp一遍即可。

时间复杂度为O(N)。

#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 1000001
#define inf 0x3f3f3f3f
using namespace std;

struct edge{
    int to,next;
    edge(){}
    edge(const int &_to,const int &_next){ to=_to,next=_next; }
}e[maxn<<1];
int head[maxn],k;
int n,val[maxn],ans;
double K;

inline int read(){
    register int x(0),f(1); register char c(getchar());
    while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
    while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
inline void add(const int &u,const int &v){ e[k]=edge(v,head[u]),head[u]=k++; }

bool flag[maxn];
int s,t;
inline void getloop(){
    for(register int i=0;i<k;i+=2){
        int u=e[i].to,v=e[i^1].to;
        if(flag[u]&&flag[v]){ s=u,t=v; return; }
        flag[u]=flag[v]=true;
    }
}

int dp[maxn][2];
bool vis[maxn];
void dfs(int u){
    vis[u]=true,dp[u][1]=val[u],dp[u][0]=0;
    for(register int i=head[u];~i;i=e[i].next){
        int v=e[i].to;
        if(vis[v]) continue;
        dfs(v),dp[u][0]+=max(dp[v][0],dp[v][1]),dp[u][1]+=dp[v][0];
    }
}

int main(){
    memset(head,-1,sizeof head);
    n=read();
    for(register int i=1;i<=n;i++) val[i]=read();
    for(register int i=1;i<=n;i++){
        int u=read()+1,v=read()+1;
        add(u,v),add(v,u);
    }
    scanf("%lf",&K);

    getloop();
    dfs(s),ans=max(ans,dp[s][0]);
    memset(vis,false,sizeof vis);
    dfs(t),ans=max(ans,dp[t][0]);
    printf("%.1lf
",ans*K);
    return 0;
}
原文地址:https://www.cnblogs.com/akura/p/11061035.html