今天的标题终于正常些,大家有没有发现(所有文字格式都用到了,嘿嘿嘿)
本人实在太咸,前几天翻刘汝佳大神的紫书时发现自己不会树形DP于是恶补了一番,这都是由于我太咸没进学校的第一梯队(运气不好,考试当天运势差,心中都是泪呀),这发个刘汝佳大神的头像和紫书封面镇楼:
初次学习当然刷模板题,我从我们学校OJ上选了一个模板题,名字是《树的直径【模板】》(多直白的模板题),题目如下:
输入格式:
输入共n行
第一行是一个正整数n,表示这棵树的结点数
接下来的n-1行,每行三个正整数a,b,w。表示结点a和结点b之间有一条边,长度为w
数据保证一定是一棵树,不必判错。
输出格式:
输出共一行
第一行仅一个数,表示这棵树的最远距离
只用管输出输入,题目是废话(呵呵呵)
这道题就是一道树的直径的裸题,树的直径指树上距离最远的两个点的距离。
旁边的zyy(我的朋友)表示:那求法呢……说这么多有啥用我要求法!!!(别管他,他第一梯队的)
求法很简单:
方法1:DP法:
开一个数组记录这棵子树的最长链,用dfs递归到叶子结点并往上推这个数组,然后在根结点处选最长链和次长链相加,再加上两个点到根节点的距离,状态转移方程:d(i)=max{d(j)+边权},由于此题边权的之不一定所以状态转移方程与紫书上不同,代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct edge
{
int v,next,w;
};
edge a[20001];
int num=0,s=0,p[10001],b[10001][3],d[10001];
//我这里的dfs返回的值是加上父亲到这个点的边权的,所以与上面的描述有点不同
int dfs(int number,int fa,int we)
{
if(!p[number])return we;
int maxe=0;
for(int i=p[number];i;i=a[i].next)
if(a[i].v!=fa)
maxe=max(maxe,dfs(a[i].v,number,a[i].w));
return maxe+we;
}
int read()
{
char c=getchar();
while(c<'0'||c>'9')c=getchar();
int x=0;
while(c>='0'&&c<='9')
{
x=x*10+c-'0';
c=getchar();
}
return x;
}
//邻接表储存
int add(int u,int v,int w)
{
a[++num].v=v;
a[num].w=w;
a[num].next=p[u];
p[u]=num;
}
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
int n,u,v,w;
n=read();
for(int i=1;i<=n-1;i++)
{
u=read(),v=read(),w=read();
add(u,v,w);
add(v,u,w);
//b数组储存根节点连接的点的编号和到那些点的距离
if(u==1)
{
b[++s][1]=v;
b[s][2]=w;
}
if(v==1)
{
b[++s][1]=u;
b[s][2]=w;
}
}
for(int i=1;i<=s;i++)
d[i]=dfs(b[i][1],1,b[i][2]);
sort(d+1,d+s+1,cmp);
//输出次长链最长链和它们到父亲节点边权的和
printf("%d",d[1]+d[2]);
return 0;
}
更新:
上面的方法是有点错误的,这里纠正一下,同时大家也思考一下,为什么会错呢?
对比下面的bfs大法,上面的这串代码就有一定的局限性,如果树的这条直径不穿过自己假设的根节点的话,测结果会变小,所以我们是要枚举每个点的。
代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct edge
{
int v,next,w;
};
edge a[200001];
int num=0,s=0,p[100001];
bool used[100001];
int dfs(int number)
{
if(!p[number])return 0;
int maxe=-0x7fffffff;
used[number]=1;
for(int i=p[number];i;i=a[i].next)
if(!used[a[i].v])
maxe=max(maxe,dfs(a[i].v)+a[i].w);
used[number]=0;
return maxe;
}
int read()
{
char c=getchar();
while(c<'0'||c>'9')c=getchar();
int x=0;
while(c>='0'&&c<='9')
{
x=x*10+c-'0';
c=getchar();
}
return x;
}
int add(int u,int v,int w)
{
a[++num].v=v;
a[num].w=w;
a[num].next=p[u];
p[u]=num;
}
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
int n,u,v,w,max1,max2,max3=-0x7fffffff;
memset(p,0,sizeof(p));
memset(used,0,sizeof(used));
n=read();
for(int i=1;i<=n-1;i++)
{
u=read(),v=read(),w=read();
if(u>=v)u^=v,v^=u,u^=v;
add(u,v,w);
}
for(int j=1;j<=n;j++)
{
used[j]=1;
max1=-0x7fffffff,max2=-0x7fffffff;
for(int i=p[j];i;i=a[i].next)
{
int k=dfs(a[i].v)+a[i].w;
if(k>max1)
{
max2=max1;
max1=k;
continue;
}
if(k>max2)
max2=k;
}
if(max3<max1+max2)max3=max1+max2;
}
printf("%d",max3);
return 0;
}
修复bug完毕!!!
但我这个似乎还可以优化
方法2:bfs大法,两次bfs,第一次任找一点开始bfs,求出离它最远的点,第二次以求出的点为起点开始bfs,求出离这个点最远的点,这段距离即为树的直径。
由于做这道题用的是DP大法,所以暂时没有bfs的代码,留着这个坑以后填。
填坑时刻:
#include"cstdio"
#include"queue"
#include"cstring"
using namespace std;
struct edge
{
int v,next,w;
};
queue<int> end;
edge a[2000001];
int num=0,s=0,p[1000001];
int dis[1000001];
bool used[1000001];
int read()
{
char c=getchar();
while(c<'0'||c>'9')c=getchar();
int x=0;
while(c>='0'&&c<='9')
{
x=x*10+c-'0';
c=getchar();
}
return x;
}
void add1(int u,int v,int w)
{
a[++num].v=v;
a[num].w=w;
a[num].next=p[u];
p[u]=num;
}
int bfs(int i)
{
end.push(i);
dis[i]=0;
used[i]=1;
while(!end.empty())
{
int fro=end.front();
for(int j=p[fro];j;j=a[j].next)
if(dis[a[j].v]>dis[fro]+a[j].w)
{
dis[a[j].v]=dis[fro]+a[j].w;
if(!used[a[j].v])
{
used[a[j].v]=1;
end.push(a[j].v);
}
}
end.pop();
used[fro]=0;
}
}
int main()
{
int n,m,u,v,w,max1=-0x7ffffff,max2; char c;
memset(p,0,sizeof(p));
n=read();
for(int i=1;i<=n;i++)
dis[i]=0x7ffffff;
for(int i=1;i<=n-1;i++)
{
u=read(),v=read(),w=read();
add1(u,v,w);
add1(v,u,w);
}
memset(used,0,sizeof(used));
bfs(1);
for(int i=1;i<=n;i++)
{
if(dis[i]>max1)max1=dis[i],max2=i;
dis[i]=0x7ffffff;
}
memset(used,0,sizeof(used));
bfs(max2);
max1=-0x7ffffff;
for(int i=1;i<=n;i++)
{
if(dis[i]>max1)max1=dis[i];
dis[i]=0x7ffffff;
}
printf("%d",max1);
return 0;
}
真是的又刮台风,一连刮三个,第三个很快又到了。(多刮点,还能停课多爽呀)
呵呵呵,第四个说好的要来,乍还没来。