星空中有n颗星星,有n-1对星星间被人为地连上了线,每条连线有各自的长度。所有星星被连成了一个整体。现在,你要在星系中找到一个最大的十字形星座。即,你要找到两条星星构成的路径,使得它们恰好有一颗公共星(这颗公共星不能是某条路径的端点),且两条路径的长度和最大。
左图红线表示了一个合法的十字形星座,而右图的星座并不合法。
输入
第一行一个数n,表示星星的数量。
接下来n行,每行3个数x,y,z,表示第x颗星星和第y颗星星间有一条连线,它的长度是z。
输出
一行,包含一个整数,表示最大的路径长度和。若答案不存在,输出-1。
样例输入
10
3 8 6
9 3 5
1 9 2
4 8 6
2 3 3
10 4 8
5 9 5
7 2 3
6 9 1
样例输出
33
提示
20%的数据n<=1000
50%的数据n<=10,000
100%的数据n<=100,000,0<=z<=1000
这个题意真是xxxx。。。。其实题意是这样的:
对于点i
1.3条i的儿子向下伸展的路径,1条i向上伸展的路径(不能回到i)
2.4条i的儿子向下伸展的路径
这样就简洁多了。对于儿子,其实就是维护每个点4个最大的儿子
f[i][0],f[i][1],f[i][2],f[i][3]表示点i第1−第4大的儿子
但是还有一种向上的情况
g[i]表示从i的父亲出发的最长路
到了i的父亲,又有两种决策,一个向上继续走,一个向下到最大的儿子
g[i]=max(g[fa[i]],f[fa[i]][0])
如果i恰好是那个最大的儿子,那么就换次大值
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
int n,x,y,z,ans,tot;
int f[100005][5],fa[100005],g[100005],son[100005];
int head[100005],Next[200005],to[200005],len[200005];
void add(int x,int y,int z)
{
tot++;
Next[tot]=head[x];
to[tot]=y;
len[tot]=z;
head[x]=tot;
}
bool cmp(int x,int y)
{
return x>y;
}
void dfs(int k,int pre) //求f数组
{
fa[k]=pre;
f[k][0]=f[k][1]=f[k][2]=f[k][3]=0;
for(int i=head[k];i!=-1;i=Next[i])
if(to[i]!=pre)
{
son[k]++;
dfs(to[i],k);
f[k][4]=f[to[i]][0]+len[i];
sort(f[k],f[k]+5,cmp);
}
}
void solve(int k,int pre) //求g数组
{
for(int i=head[k];i!=-1;i=Next[i])
if(to[i]!=pre)
{
if(f[to[i]][0]+len[i]==f[k][0]) //判断是否为最大的儿子
g[to[i]]=max(g[k],f[k][1])+len[i];
else
g[to[i]]=max(g[k],f[k][0])+len[i];
solve(to[i],k);
}
}
void dp(int k,int pre)
{
if(son[k]>=4) ans=max(ans,f[k][0]+f[k][1]+f[k][2]+f[k][3]); //注意必须要判儿子个数,验证答案是否合法
if(son[k]>=3&&pre!=0) ans=max(ans,f[k][0]+f[k][1]+f[k][2]+g[k]);
for(int i=head[k];i!=-1;i=Next[i])
if(to[i]!=pre) dp(to[i],k);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) head[i]=-1;
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dfs(1,0);
solve(1,0);
dp(1,0);
cout<<ans;
return 0;
}
今天考试心不在焉,状态一落千丈。不过我还是想说,这题的题意真是xxxx。。。。