zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    LIS例题
    基数排序板子
    lower_bound和upper_bound在刷leetcode的时候...
    leetcode1081/316 求字典序最小的包含所有出现字符一次的子序列
    PHP 求多个数组的笛卡尔积,适用于求商品规格组合 【深度优先搜索】【原创】
    PHP 求多个数组的笛卡尔积,适用于求商品规格组合【原创】
    Spring 中注入 properties 中的值
    Java 枚举活用
    Intellij IDEA 快捷键整理(TonyCody)
    WIN API -- 2.Hello World
  • 原文地址:https://www.cnblogs.com/akura/p/11061035.html
Copyright © 2011-2022 走看看