树形DP初步(1)

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

本人实在太咸,前几天翻刘汝佳大神的紫书时发现自己不会树形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;
}


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

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


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值