题目背景
一座城市,往往会被人们划分为几个区域,例如住宅区、商业区、工业区等等。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
题解
我们发现直接Dp不行,因为存在环,考虑拆环,我们对任意一个点进行dfs,当到达的点已经到达过时,就说明这条边位于环中,由于题目只存在一个环,我们把这条边和它的反向边标记,分别进行dp就可以了。
Dp的话就是树形Dp
f[i][0]
f
[
i
]
[
0
]
表示该点不选时的最大值,可以从
f[i][0],f[i][1]
f
[
i
]
[
0
]
,
f
[
i
]
[
1
]
转移过来,
f[i][1]
f
[
i
]
[
1
]
表示改点选时的最大值,只能从
f[j][0],j∈E
f
[
j
]
[
0
]
,
j
∈
E
转移过来。、
代码
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
const int MAXN=1e6;
using namespace std;
struct Node
{
int next,to;
}edge[MAXN];
int head[MAXN],cnt,f[MAXN][2],n,x,y,z,flag,vis[MAXN],begin_1,begin_2,Delete,val[MAXN];
double k;
inline void Add_Edge(int u,int v)
{
edge[cnt]=(Node){head[u],v};
head[u]=cnt++;
}
void dfs(int x,int fa)//拆环
{
if(flag) return ;
for(int i=head[x];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(vis[v]==1&&v!=fa)
{
begin_1=x;
begin_2=v;
flag=1;
Delete=i;
//cout<<Delete;
return ;
}
if(vis[v]==0&&v!=fa)
{
vis[v]=1;
dfs(v,x);
}
}
}
void dp(int x,int fa)//树形Dp
{
f[x][0]=0;f[x][1]=val[x];
for(int i=head[x];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(v!=fa&&i!=Delete&&(i^1)!=Delete)
{
dp(v,x);
f[x][0]+=max(f[v][1],f[v][0]);
f[x][1]+=f[v][0];
}
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&val[i]);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
Add_Edge(x+1,y+1);
Add_Edge(y+1,x+1);
}
scanf("%lf",&k);
dfs(1,0);
dp(begin_1,0);
int ans=0;
ans=max(ans,f[begin_1][0]);
dp(begin_2,0);
printf("%.1f",(double)k*max(ans,f[begin_2][0]));
return 0;
}