树形DP初步(1)

今天的标题终于正常些,大家有没有发现(所有文字格式都用到了,嘿嘿嘿)

本人实在太咸,前几天翻刘汝佳大神的紫书时发现自己不会树形DP于是恶补了一番,这都是由于我太咸没进学校的第一梯队(运气不好,考试当天运势差,心中都是泪呀),这发个刘汝佳大神的头像和紫书封面镇楼:


初次学习当然刷模板题,我从我们学校OJ上选了一个模板题,名字是《树的直径【模板】》(多直白的模板题),题目如下:

输入格式:

输入共n行
第一行是一个正整数n,表示这棵树的结点数
接下来的n-1行,每行三个正整数a,b,w。表示结点a和结点b之间有一条边,长度为w
数据保证一定是一棵树,不必判错。

输出格式:

输出共一行
第一行仅一个数,表示这棵树的最远距离


只用管输出输入,题目是废话(呵呵呵)


这道题就是一道树的直径的裸题,树的直径指树上距离最远的两个点的距离。

旁边的zyy(我的朋友)表示:那求法呢……说这么多有啥用我要求法!!!(别管他,他第一梯队的)

求法很简单:

方法1:DP法:

开一个数组记录这棵子树的最长链,用dfs递归到叶子结点并往上推这个数组,然后在根结点处选最长链和次长链相加,再加上两个点到根节点的距离,状态转移方程:d(i)=max{d(j)+边权},由于此题边权的之不一定所以状态转移方程与紫书上不同,代码如下:

#include
#include
#include
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
#include
#include
#include
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
修复bug完毕!!!
但我这个似乎还可以优化

方法2:bfs大法,两次bfs,第一次任找一点开始bfs,求出离它最远的点,第二次以求出的点为起点开始bfs,求出离这个点最远的点,这段距离即为树的直径。

由于做这道题用的是DP大法,所以暂时没有bfs的代码,留着这个坑以后填。

填坑时刻:


#include"cstdio"
#include"queue"
#include"cstring"
using namespace std;
struct edge  
{  
    int v,next,w;  
};  
queue 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;
}


真是的又刮台风,一连刮三个,第三个很快又到了。(多刮点,还能停课多爽呀)

呵呵呵,第四个说好的要来,乍还没来。


你可能感兴趣的:(树形DP)