BSOJ3760||洛谷P1453 城市环路 题解
城市环路
Description
一座城市,往往会被人们划分为几个区域,例如住宅区、商业区、工业区等等。B市就被分为了以下的两个区域——城市中心和城市郊区。在着这两个区域的中间是一条围绕B市的环路,环路之内便是B市中心。
整个城市可以看做一个N个点,N条边的单圈图(保证图连通),唯一的环便是绕城的环路。保证环上任意两点有且只有2条路径互通。图中的其它部分皆隶属城市郊区。
现在,有一位名叫Jim的同学想在B市开店,但是任意一条边的2个点不能同时开店,每个点都有一定的人流量Pi,在该点开店的利润就等于该店的人流量Pi×K(K≤10000),K的值将给出。
Jim想尽量多的赚取利润,请问他应该在哪些地方开店?
整个城市可以看做一个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。
第二行N个正整数,表示每个点的人流量Pi(Pi≤10000)。
下面N行,每行2个整数A,B,表示A,B建有一条双向路。
最后一行一个实数K。
Output
输出仅一个实数M,(保留1位小数),代表开店的最大利润。
Sample Input
4
1 2 1 5
0 1
0 2
1 2
1 3
2
Sample Output
12.0
Hint
【数据范围】
对于20%的数据 N≤100.
对于50%的数据 N≤2000.
对于100%的数据 N≤100000.
对于20%的数据 N≤100.
对于50%的数据 N≤2000.
对于100%的数据 N≤100000.
↑以上是正经的题目描述XD
↓接下来是一点也不正经的题解啦
题目已经很明确了,这是个环,环上还有树。
就像这样:

乍一看会很麻烦对吧......那么我们先来考虑一下比较简单的情况。假如这一开始就只是个环呢?
那很简单啊,只要随便找个点开始,选择它跑一圈,不选它再跑一圈,取个最大值就行了嘛。
那么,加上树有什么影响呢?
没有。对,没有。只要提前把这个点引出去的树处理好就行了。
也就是说,只需要先对这棵子树进行一次动态规划,然后把这里能取到的最大值当做这个点的权值,这题就成了环形DP裸题啦23333。
主要流程:
①:Tarjan法先找出图中唯一的环。
②:枚举环上的每个点进行树形DP。
③:跑一遍环上DP。
听说这个东西好像叫环套树来着...去看了看其他几道环套树都好可怕啊QAQ
AC Code:
View 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; }
一开始忘了环形要跑两遍...真是⑨一般的错误啊(笑)
posted on 2017-06-17 15:35 Marshmallow 阅读(308) 评论(0) 收藏 举报
浙公网安备 33010602011771号