首页 > 代码库 > BSOJ3760||洛谷P1453 城市环路 题解

BSOJ3760||洛谷P1453 城市环路 题解

城市环路

Description

  一座城市,往往会被人们划分为几个区域,例如住宅区、商业区、工业区等等。B市就被分为了以下的两个区域——城市中心和城市郊区。在着这两个区域的中间是一条围绕B市的环路,环路之内便是B市中心。
  整个城市可以看做一个N个点,N条边的单圈图(保证图连通),唯一的环便是绕城的环路。保证环上任意两点有且只有2条路径互通。图中的其它部分皆隶属城市郊区。
  现在,有一位名叫Jim的同学想在B市开店,但是任意一条边的2个点不能同时开店,每个点都有一定的人流量Pi,在该点开店的利润就等于该店的人流量Pi×K(K≤10000),K的值将给出。
  Jim想尽量多的赚取利润,请问他应该在哪些地方开店?

Input

  第一行一个整数N 代表城市中点的个数。城市中的N个点由0~N-1编号。
  第二行N个正整数,表示每个点的人流量Pi(Pi≤10000)。
  下面N行,每行2个整数A,B,表示A,B建有一条双向路。
  最后一行一个实数K。

Output

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

Sample Input

41 2 1 50 10 21 21 32

Sample Output

12.0

Hint

【数据范围】
  对于20%的数据 N≤100.
  对于50%的数据 N≤2000.
  对于100%的数据 N≤100000.
 
↑以上是正经的题目描述XD
↓接下来是一点也不正经的题解啦
 
题目已经很明确了,这是个环,环上还有树。
就像这样:
技术分享
 
乍一看会很麻烦对吧......那么我们先来考虑一下比较简单的情况。假如这一开始就只是个环呢?
那很简单啊,只要随便找个点开始,选择它跑一圈,不选它再跑一圈,取个最大值就行了嘛。
那么,加上树有什么影响呢?
没有。对,没有。只要提前把这个点引出去的树处理好就行了。
也就是说,只需要先对这棵子树进行一次动态规划,然后把这里能取到的最大值当做这个点的权值,这题就成了环形DP裸题啦23333。
 
主要流程:
①:Tarjan法先找出图中唯一的环。
②:枚举环上的每个点进行树形DP。
③:跑一遍环上DP。
 
听说这个东西好像叫环套树来着...去看了看其他几道环套树都好可怕啊QAQ
 
AC Code:
技术分享
#include<cstring>#include<cstdio>#include<cmath>#include<stack>#include<iostream>#include<algorithm>using namespace std;inline const int Read(){    int Num=0,Sgn=1;    char ch=getchar();    while(!isdigit(ch)){if(ch==-)Sgn=-1;ch=getchar();}    while(isdigit(ch)){Num=(Num<<1)+(Num<<3)+ch-0;ch=getchar();}    return Num*Sgn;}struct Edge{    int Next,To;}e[200005];int h[200005]={0},Cnt=0;void Addedge(int x,int y){e[++Cnt]=(Edge){h[x],y}; h[x]=Cnt;}int a[200005]={0};double K;int N;int Scnt=0;int DFN[200005]={0},Lowlink[200005]={0};int Cir[200005]={0},Belong[200005]={0},Prt[200005]={0};int f[200005][2]={0},g[200005][2]={0};stack<int>Sta;void Tarjan(int x){    Scnt++;    DFN[x]=Scnt; Lowlink[x]=Scnt;    Sta.push(x);    for(int i=h[x];i;i=e[i].Next)    {        int y=e[i].To;        if(!DFN[y])        {            Prt[y]=x;            Tarjan(y);            Lowlink[x]=min(Lowlink[x],Lowlink[y]);        }        else if(Prt[x]!=y)          Lowlink[x]=min(Lowlink[x],DFN[y]);    }    if(Lowlink[x]==DFN[x])    {        if(Sta.top()==x)        {            Sta.pop();            return;        }        int t;        do        {            t=Sta.top(); Sta.pop();            Cir[++Cir[0]]=t;            Belong[t]=1;        }while(t!=x);    }}void DFS(int x){    f[x][0]=0; f[x][1]=a[x];    for(int i=h[x];i;i=e[i].Next)    {        int y=e[i].To;        if(Prt[x]!=y&&!Belong[y])        {            Prt[y]=x;            DFS(y);            f[x][0]+=max(f[y][1],f[y][0]);            f[x][1]+=f[y][0];        }    }}int main(){    N=Read();    for(int i=1;i<=N;i++)      a[i]=Read();    for(int i=1;i<=N;i++)    {        int Tx=Read(),Ty=Read();        Addedge(Tx+1,Ty+1); Addedge(Ty+1,Tx+1);    }    scanf("%lf",&K);    Tarjan(1);    int M=Cir[0];    memset(Prt,0,sizeof(Prt));    for(int i=1;i<=M;i++)      DFS(Cir[i]);    for(int i=1;i<=M;i++)    {        g[i][0]=f[Cir[i]][0];        g[i][1]=f[Cir[i]][1];    }        f[2][0]=g[1][0]+g[2][0];    f[2][1]=g[1][0]+g[2][1];    for(int i=3;i<=M;i++)    {        f[i][1]=f[i-1][0]+g[i][1];        f[i][0]=max(f[i-1][0],f[i-1][1])+g[i][0];    }    int Ans=0;    Ans=max(f[M][1],max(Ans,f[M][0]));    f[2][0]=g[1][1]+g[2][0];    f[2][1]=-0x3f3f3f3f;    for(int i=3;i<=M;i++)    {        f[i][0]=max(f[i-1][0],f[i-1][1])+g[i][0];        f[i][1]=f[i-1][0]+g[i][1];    }    Ans=max(Ans,f[M][0]);    printf("%.1lf\n",(double)Ans*K);    return 0;}
View Code

 

一开始忘了环形要跑两遍...真是⑨一般的错误啊(笑)

BSOJ3760||洛谷P1453 城市环路 题解