http://acm.hdu.edu.cn/showproblem.php?pid=3534
题意:给出一颗树(n个顶点,n-1条边)
求最长的路径,以及最长路径的条数。
路径无非就是连接两个点直接的路。
因为是一颗树,所以连接两个点肯定是唯一的路径。
其实就是求两点间距离的最大值,以及这个最大值有多少个。
很裸的树形DP;
首先统计出结点到叶子结点的最长距离和次长距离。
然后找寻经过这个点的,在这个为根结点的子树中的最长路径个数目。
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stdio.h>
using namespace std;
const int MAXN=100010;
const int INF=0x3f3f3f3f;
struct Node
{
int to,next,len;
}edge[MAXN*2];
int head[MAXN];
int tol;
int maxn[MAXN];//该节点往下到叶子结点的最大距离
int smaxn[MAXN];// 次大距离
int maxn_num[MAXN];//最大距离的个数
int smaxn_num[MAXN];//次大距离的个数
int path[MAXN];//该结点为根的子树中,包含该结点的最长路径长度
int num[MAXN];//最长路径的长度
void init()
{
tol=0;
memset(head,-1,sizeof(head));
}
void add(int u,int v,int w)
{
edge[tol].to=v;
edge[tol].len=w;
edge[tol].next=head[u];
head[u]=tol++;
edge[tol].to=u;
edge[tol].len=w;
edge[tol].next=head[v];
head[v]=tol++;
}
void dfs(int u,int pre)
{
maxn[u]=smaxn[u]=0;
maxn_num[u]=smaxn_num[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(v==pre)continue;
dfs(v,u);
if(maxn[v]+edge[i].len>maxn[u])
{
smaxn[u]=maxn[u];
smaxn_num[u]=maxn_num[u];
maxn[u]=maxn[v]+edge[i].len;
maxn_num[u]=maxn_num[v];
}
else if(maxn[v]+edge[i].len==maxn[u])
{
maxn_num[u]+=maxn_num[v];
}
else if(maxn[v]+edge[i].len>smaxn[u])
{
smaxn[u]=maxn[v]+edge[i].len;
smaxn_num[u]=maxn_num[v];
}
else if(maxn[v]+edge[i].len==smaxn[u])
{
smaxn_num[u]+=maxn_num[v];
}
}
if(maxn_num[u]==0)//叶子结点
{
maxn[u]=smaxn[u]=0;
maxn_num[u]=smaxn_num[u]=1;
path[u]=0;
num[u]=1;
return;
}
int c1=0,c2=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(v==pre)continue;
if(maxn[u]==maxn[v]+edge[i].len)c1++;
else if(smaxn[u]==maxn[v]+edge[i].len)c2++;
}
path[u]=0;
num[u]=0;
if(c1>=2)//最长+最长
{
int tmp=0;
path[u]=maxn[u]*2;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(v==pre)continue;
if(maxn[u]==maxn[v]+edge[i].len)
{
num[u]+=tmp*maxn_num[v];
tmp+=maxn_num[v];
}
}
}
else if(c1>=1 && c2>=1)//最长+次长
{
path[u]=maxn[u]+smaxn[u];
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(v==pre)continue;
if(maxn[u]==maxn[v]+edge[i].len)
{
num[u]+=maxn_num[v]*smaxn_num[u];
}
}
}
else//最长
{
path[u]=maxn[u];
num[u]=maxn_num[u];
}
}
int main()
{
int n;
while(scanf("%d",&n)==1)
{
int u,v,w;
init();
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
dfs(1,-1);
int ans1=0,ans2=0;
for(int i=1;i<=n;i++)
{
if(path[i]>ans1)
{
ans1=path[i];
ans2=num[i];
}
else if(path[i]==ans1)
ans2+=num[i];
}
printf("%d %d\n",ans1,ans2);
}
return 0;
}
问树的直径及直径的个数。对每个点来说,经过它的最长路径有3种构成
1° 只有一个儿子,而且到父亲的距离短
2° 有多个儿子,其中的两个儿子与它相连
3° 一个儿子连上它和父亲
对前两种,就是
max[now]
,但是对第三种,如果算为max[now]
的话,那么还会在max[father]
处算一遍,所以max[now]
中不包含父亲上的信息。然后现在
cnt[now]
有三种情况1°最长边只有一个,次长边没有,
cnt[now] = 1
2°最长边只有一个,次长边有,
cnt[now] = 次长边的个数
3°最长边有多个,
cnt[now] = blablabla……
(太长了,,,看代码吧)由于不需要考虑父亲,所以一次dfs解决
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <vector>
#include <queue>
#include <deque>
#include <utility>
#include <map>
#include <set>
#include <cctype>
#include <climits>
#include <stack>
#include <cmath>
#include <bitset>
#include <numeric>
#include <functional>
using namespace std;
int tail[10010], to[10010<<1], pre[10010<<1], cost[10010<<1], tot;
inline void add(int _from, int _to, int _cost) {
pre[tot] = tail[_from];
to[tot] = _to;
cost[tot] = _cost;
tail[_from] = tot++;
}
int first[10010], first_cnt[10010], second[10010], second_cnt[10010];
int dia[10010], cnt[10010];
void dfs(int now, int fa) {
first[now] = first_cnt[now] = second[now] = second_cnt[now] = 0;
dia[now] = cnt[now] = 0;
for(int i = tail[now]; i != -1; i = pre[i]) {
int _to = to[i], _cost = cost[i];
if(_to == fa)
continue;
dfs(_to, now);
if(first[now] < first[_to] + _cost) {
second[now] = first[now];
second_cnt[now] = first_cnt[now];
first[now] = first[_to] + _cost;
first_cnt[now] = first_cnt[_to];
if(!second_cnt[now])
dia[now] = first[now], cnt[now] = first_cnt[now];
else
dia[now] = first[now] + second[now], cnt[now] = first_cnt[now]*second_cnt[now];
} else if(first[now] == first[_to] + _cost) {
if((dia[now]>>1) == first[now])
cnt[now] += first_cnt[now] * first_cnt[_to];
else
cnt[now] = first_cnt[now] * first_cnt[_to], dia[now] = first[now]<<1;
first_cnt[now] += first_cnt[_to];
} else if(second[now] < first[_to] + _cost) {
second[now] = first[_to] + _cost;
second_cnt[now] = first_cnt[_to];
if((dia[now]>>1) != first[now])
dia[now] = first[now] + second[now], cnt[now] = first_cnt[now]*second_cnt[now];
} else if(second[now] == first[_to] + _cost) {
second_cnt[now] += first_cnt[_to];
if((dia[now]>>1) != first[now])
cnt[now] = first_cnt[now]*second_cnt[now];
}
}
if(!first_cnt[now])
first_cnt[now] = cnt[now] = 1;
}
int main() {
int n;
while(scanf("%d", &n) > 0) {
tot = 0;
memset(tail, -1, sizeof tail);
for(int i = 1; i < n; i++) {
int _u, _v, _cost;
scanf("%d%d%d", &_u, &_v, &_cost);
add(_u, _v, _cost);
add(_v, _u, _cost);
}
dfs(1, -1);
int maxx = 0, ans = 0;
for(int i = 1; i <= n; i++) {
if(maxx < dia[i])
maxx = dia[i], ans = cnt[i];
else if(maxx == dia[i])
ans += cnt[i];
}
printf("%d %d\n", maxx, ans);
}
return 0;
}
情况1:由某叶子节点出发产生的最长路径直接构成
情况2:由某有多个儿子的节点出发产生的两条长路径组成,这其中,又可以分为两条长路径长度相等与否两种情况
所以 在dp的时候,我们需要记录每个节点出发产生的最长路径和次长路径,以及他们的数量,数量的统计也是非常麻烦
详细请见代码:
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#include<math.h>
#include<ctype.h>
#include<algorithm>
#include<string>
#include<string.h>
#include<queue>
#define mod 998244353
#define MAX 100000000
using namespace std;
int t,n,m,p,k,tt,f;
int x;
int head[10010];
typedef struct Node
{
int en;
int value;
int next;
}node;
node edge[20010];
typedef struct DPnode
{
int dp1,dp2,len,nn;
int n1,n2;
}DP;
DP dp[10010];
void ini()
{
int x,y,z;
for(int i=1;i<=n-1;i++)
{
scanf("%d%d%d",&x,&y,&k);
edge[2*i-1].en=y;
edge[2*i-1].next=head[x];
edge[2*i-1].value=k;
head[x]=2*i-1;
edge[2*i].en=x;
edge[2*i].next=head[y];
edge[2*i].value=k;
head[y]=2*i;
}
}
void dfs(int s,int p)
{
dp[s].dp1=dp[s].dp2=dp[s].len=dp[s].n1=dp[s].n2=dp[s].nn=0;
int leaf=1;
for(int i=head[s];i;i=edge[i].next)
{
int q=edge[i].en;
if(q==p)
continue;
leaf=0;
dfs(q,s);
int tmp=dp[q].dp1+edge[i].value;
if(tmp>dp[s].dp1)
{
dp[s].dp2=dp[s].dp1;
dp[s].n2=dp[s].n1;
dp[s].dp1=tmp;
dp[s].n1=dp[q].n1;
}
else if(tmp==dp[s].dp1)
{
dp[s].n1+=dp[q].n1;
}
else if(tmp>dp[s].dp2)
{
dp[s].dp2=tmp;
dp[s].n2=dp[q].n1;
}
else if(tmp==dp[s].dp2)
{
dp[s].n2+=dp[q].n1;
}
}
if(leaf)
{
dp[s].n1=1;dp[s].nn=1;
dp[s].len=0;
dp[s].dp1=0;
return;
}
int c1=0,c2=0;
for(int i=head[s];i;i=edge[i].next)
{
int q=edge[i].en;
if(q==p)
continue;
int tmp=dp[q].dp1+edge[i].value;
if(tmp==dp[s].dp1)
c1++;
else if(tmp==dp[s].dp2&&dp[s].dp2)
c2++;
}
if(c1>1)
{
dp[s].len=dp[s].dp1*2;
int sum=0;
for(int i=head[s];i;i=edge[i].next)
{
int q=edge[i].en;
if(q==p)
continue;
if(dp[q].dp1+edge[i].value==dp[s].dp1)
{
dp[s].nn+=sum*dp[q].n1;
sum+=dp[q].n1;
}
}
}
else if(c2>0)
{
dp[s].len=dp[s].dp1+dp[s].dp2;
for(int i=head[s];i;i=edge[i].next)
{
int q=edge[i].en;
if(q==p)
continue;
if(dp[q].dp1+edge[i].value==dp[s].dp2)
{
dp[s].nn+=dp[s].n1*dp[q].n1;
}
}
}
else
{
dp[s].len=dp[s].dp1;
dp[s].nn=dp[s].n1;
}
return ;
}
void solve()
{
int ans=0;
int num=0;
for(int i=1;i<=n;i++)
{
if(dp[i].len>ans)
{
ans=dp[i].len;
num=dp[i].nn;
}
else if(dp[i].len==ans)
{
num+=dp[i].nn;
}
}
printf("%d %d\n",ans,num);
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
memset(head,0,sizeof(head));
ini();
dfs(1,0);
solve();
}
return 0;
}
思路:最长边以前做过,用树形DP可以。求最长边的时候可以统计一下最长边的个数,那么以u为根节点的子树中最长边为:Len[u]+max(len[v]),个数node[u]*node[v]。
#include<stdio.h>
#include<string.h>
const int N=500000;
const int inf=0x3fffffff;
int head[N],num,Len[N],ans,node[N],k;
struct edge
{
int ed,w,next;
}e[N*2];
void addedge(int x,int y,int w)
{
e[num].ed=y;e[num].w=w;e[num].next=head[x];head[x]=num++;
e[num].ed=x;e[num].w=w;e[num].next=head[y];head[y]=num++;
}
void dfs(int u,int fa)
{
int i,v,temp;
Len[u]=0;//最长边
node[u]=1;//最长边的个数
for(i=head[u];i!=-1;i=e[i].next)
{
v=e[i].ed;
if(v==fa)continue;
dfs(v,u);
temp=Len[v]+e[i].w;
if(temp+Len[u]>ans)//最长边经过v
{
k=node[v]*node[u];
ans=temp+Len[u];
}
else if(temp+Len[u]==ans)
k+=node[v]*node[u];
if(Len[u]<temp)//更新最长边
{
Len[u]=temp;
node[u]=node[v];
}
else if(Len[u]==temp)//更新最长边的个数
node[u]+=node[v];
}
}
int main()
{
int n,i,x,y,w;
while(scanf("%d",&n)!=-1)
{
memset(head,-1,sizeof(head));
num=0;
for(i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&w);
addedge(x,y,w);
}
ans=-inf;k=0;
dfs(1,0);
printf("%d %d\n",ans,k);
}
return 0;
}