题目来源:
https://www.luogu.org/problemnew/show/P1453
题目描述:
题目背景
一座城市,往往会被人们划分为几个区域,例如住宅区、商业区、工业区等等。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
说明
【数据范围】
对于20%的数据,N≤100.
对于另外20%的数据,环上的点不超过2000个
对于50%的数据 N≤50000.
对于100%的数据 N≤100000.
解题思路:
首先题目的图是一个环加上很多棵树,题目要求一个边不能有两个点同时选,这个和那个上司舞会那题差不多,都是树形dp,有dp【i】【0】和dp【i】【1】来表示状态,dp【i】【0】+=max(dp【v】【1】,dp【v】【0】),dp【i】【1】+=dp【v】【0】;v是i到的点,因为有个环,我们需要把环上相邻两点S,T,拆边,分别以S,T为根,就行树形dp,最后取max(dp【S】【0】,dp【T】【0】)就行,因为这样才能保证,S,T不能同时选。。。我是用并查集来判断环的。。
代码:
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn=1e5+10;
int n,w[maxn],head[maxn],cnt=0,dp[maxn][2],vis[maxn],fa[maxn];
struct newt
{
int to,next;
}e[2*maxn];
void addedge(int u,int v)
{
e[cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt++;
}
int fi(int x)
{
if(fa[x]==x)return x;
return fa[x]=fi(fa[x]);
}
void dfs(int u,int f)
{
dp[u][1]=w[u],dp[u][0]=0;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(v==f)continue;
dfs(v,u);
dp[u][1]+=dp[v][0];
dp[u][0]+=max(dp[v][0],dp[v][1]);
}
}
int main()
{
int S,T;
double k;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&w[i]),fa[i]=i,head[i]=-1;
for(int i=1;i<=n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
int f1=fi(a),f2=fi(b);
if(f1==f2){
S=a,T=b;continue;;
}
fa[f1]=f2;
addedge(a+1,b+1);
addedge(b+1,a+1);
}
scanf("%lf",&k);
dfs(S,S);
double ans1=dp[S][0],ans2;
dfs(T,T);
ans2=dp[T][0];
printf("%.1lf\n",max(ans1,ans2)*k);
return 0;
}