题面
题意
给出一幅有n个点,n条边的无向联通图,每一个点有一个权,并给出一个常数k,对其中一些点进行染色,且相邻两点的颜色不能都染,则最大的染色点权值和*k是多少.
分析
首先题目与k几乎无关(最后乘上即可),仅仅比树多了一条边,因而只有一个环,只要处理一下这个唯一环即可用树形dp的思路来做.
可以在这个环上找任意两个相邻点(用dfs找,第一个扫过两次的点就在环上),记为s和t,然后删掉两点之间的这条边(标记一下),然后就是一棵有限制的树了,以s为根节点,分以下两种情况进行讨论:
不对s进行染色,则之后均没有限制.
对s进行染色,则t不能再染色.
两种情况取最大值即可.
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100100
#define db double
using namespace std;
struct Bn
{
int next,to;
};
Bn bn[N*2];
int n,ans,ll[N],bb,first[N],s,t,dp[N][2];
bool vis[N],get;
db k;
inline void add(int u,int v)
{
bb++;
bn[bb].next=first[u];
bn[bb].to=v;
first[u]=bb;
}
void find(int now,int last)
{
if(get) return;
int p=first[now];
for(;p!=-1;p=bn[p].next)
{
if(bn[p].to==last) continue;
if(vis[bn[p].to]==1)
{
s=now;
t=bn[p].to;
get=1;
return;
}
find(bn[p].to,now);
}
}
int dfs(int now,bool rs,int last)
{
if(dp[now][rs]!=-1) return dp[now][rs];
int i,j,res=0,p=first[now];
if(rs)
{
for(;p!=-1;p=bn[p].next)
{
if(bn[p].to==last||bn[p].to==-1) continue;
res+=dfs(bn[p].to,0,now);
}
}
else
{
for(;p!=-1;p=bn[p].next)
{
if(bn[p].to==last||bn[p].to==-1) continue;
res+=max(dfs(bn[p].to,0,now),dfs(bn[p].to,1,now)+ll[bn[p].to]);
}
}
dp[now][rs]=res;
return res;
}
int main()
{
register int i,j,p,q;
memset(first,-1,sizeof(first));
memset(dp,-1,sizeof(dp));
cin>>n;
for(i=0;i<n;i++)
{
scanf("%d",&ll[i]);
}
for(i=1;i<=n;i++)
{
scanf("%d%d",&p,&q);
add(p,q);
add(q,p);
}
vis[1]=1;
find(1,-1);
p=first[s];
for(;p!=-1;p=bn[p].next)
{
if(bn[p].to==t)
{
bn[p].to=-1;
break;
}
}
p=first[t];
for(;p!=-1;p=bn[p].next)
{
if(bn[p].to==s)
{
bn[p].to=-1;
break;
}
}
ans=dfs(s,0,-1);
memset(dp,-1,sizeof(dp));
dp[t][1]=-100000000;
ans=max(ans,dfs(s,1,-1)+ll[s]);
cin>>k;
printf("%.1lf",k*ans);
}