携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第22天,点击查看活动详情
城市环路
题目背景
一座城市,往往会被人们划分为几个区域,例如住宅区、商业区、工业区等等。
B 市就被分为了以下的两个区域——城市中心和城市郊区。在这两个区域的中间是一条围绕 B 市的环路,环路之内便是 B 市中心。
题目描述
整个城市可以看做一个 个点, 条边的单圈图(保证图连通),唯一的环便是绕城的环路。保证环上任意两点有且只有 条简单路径互通。图中的其它部分皆隶属城市郊区。
现在,有一位名叫 Jim 的同学想在 B 市开店,但是任意一条边的 个点不能同时开店,每个点都有一定的人流量,第 个点的人流量是 ,在该点开店的利润就等于 ,其中 是一个常数。
Jim 想尽量多的赚取利润,请问他应该在哪些地方开店?
输入格式
第一行一个整数 ,代表城市中点的个数。城市中的 个点由 编号。
第二行有 个整数,第 个整数表示第 个点的人流量 。
接下来 行,每行有两个整数 ,代表存在一条连接 和 的道路。
最后一行有一个实数,代表常数 。
输出格式
输出一行一个实数代表答案,结果保留一位小数。
样例 #1
样例输入 #1
4
1 2 1 5
0 1
0 2
1 2
1 3
2
样例输出 #1
12.0
提示
数据规模与约定
- 对于 的数据,保证 。
- 另有 的数据,保证环上的点不超过 个。
- 对于 的数据,保证 ,,,, 的小数点后最多有 位数字。
#include <bits/stdc++.h>
using namespace std;
int n,u,v,ans=0;
double k;
int cnt=0;
int w[200005];
struct edge{
int num;
int next;
}p[200005];
int head[200005]={0};
void add(int a,int b){
p[++cnt].num=b;
p[cnt].next=head[a];
head[a]=cnt;
}
bool flag=0;
bool vis[100005]={0};
int dp[100005][2]={0};
void dfs(int u,int pre,int no){
dp[u][1]=w[u];
dp[u][0]=0;
for(int i=head[u];i;i=p[i].next){
int v=p[i].num;
if(v==pre){//跳过父节点
continue;
}
if(v!=no){
dfs(v,u,no);
dp[u][1]+=dp[v][0];//转移状态,取 不取上个点的情况
dp[u][0]+=max(dp[v][0],dp[v][1]);//取 上个点的两状态的最大值
}
}
}
void fc(int x,int pre){
if(flag){//假如已经找到了环,继续就没有意义
return ;
}
if(!vis[x]){//vis记录有没有走过
vis[x]=1;
}
for(int i=head[x];i;i=p[i].next){
int v=p[i].num;
if(v==pre){
continue;
}
if(!vis[v]){
fc(v,x);
}else{//x v在环上
flag=1;
//相当于断开x v,分别以两点为根节点dp
dfs(x,v,x);//以x为起点,记录x不会作为子节点
ans=max(ans,dp[x][0]);
dfs(v,x,v);//以v为起点,记录x不会作为子节点
ans=max(ans,dp[v][0]);
//最终答案相当于是不取u或者不取v的答案的最大值,因为显然两者不能同时取
return;
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&w[i]);//每个点的价值
}
for(int i=1;i<=n;i++){
scanf("%d%d",&u,&v);
u++;//处理为1-n
v++;
add(u,v);//连接u v,类似链式前向星
add(v,u);
}
scanf("%lf",&k);
fc(1,1);//从一号点开始找环,先默认它的祖先是自己
double answer=(double)ans*k;//利润是k倍的人流量
printf("%.1lf",answer);
return 0;
}