hdoj 2586 <LCA转RMQ算法--学习之路>


题目链接: hdoj 2586


思路:

先找一点作为起点通过DFS回溯记录每次DFS的点数(就是从根节点遍历所有点的去路和回来时的路)

用vis [  ] 数组记录每次走过的是哪一个点--

用D [ ] 来对应vis [ ] 数组上的点的深度(与根节点的距离)

用R [ ] 来记录每个点第一次出现的位置(两点的最初位置之间(含他们自身)的点中深度最小的点就是他们的最近祖先)

然后查找RMQ算法就可以查到最近祖先了---

样例:



我这个程序是让n当根节点--(因为这一题让1当根节点,,代码错的也会过--不便于学习新知识--(这题数据太弱))

看倒数第二行:回溯的顺序就是13-10-12-10-7------

最后一行就是回溯的每个位置的点的深度(用RMQ时,就是根据这个比较的)

倒数第四行是每个点第一次出现的位置--

比如求3和4的最近祖先时:

3第一次出现在8的位置,4是18的位置--然后我们在最后一行8-18中找最小的深度()


找到最小深度4--再看倒数第二行发现对应的是3----就是说明点3是3和4的最近祖先


回溯过程图:




代码上的中间值便于理解这个方法,所以就没有删除--

代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define MA 420000
int n,m;
struct node{
	int to,w,next;
}edge[MA*2];
int head[MA];
int D[MA];//深度 
int R[MA];//每个点第一次出现的位置 
int QM[MA][33];
int vis[MA*2],kp;//vis记录第i个位置的数 
int dis[MA];
void bfs(int now,int fa,int chang)
{
	dis[now]=chang;
	for (int j=head[now];j!=-1;j=edge[j].next)
	{
		if (edge[j].to==fa) continue;
		bfs(edge[j].to,now,chang+edge[j].w);
	}
}
void DFS(int now,int fa,int deep)
{
	D[kp]=deep;
	R[now]=kp;
	vis[kp++]=now;
	for (int j=head[now];j!=-1;j=edge[j].next)
	{
		if (edge[j].to==fa) continue;
		DFS(edge[j].to,now,deep+1);
		D[kp]=deep;
		vis[kp++]=now;
	}	
}
int main()
{
	int t;scanf("%d",&t);
	while (t--)
	{
		scanf("%d%d",&n,&m);
		int a,b,c;
		for (int i=1;i<=n;i++)
		head[i]=-1;
		for (int i=0;i<n-1;i++)
		{
			scanf("%d%d%d",&a,&b,&c);
			edge[i*2].to=b;edge[i*2].w=c;
			edge[i*2].next=head[a];
			head[a]=i*2;
			edge[i*2+1].to=a;edge[i*2+1].w=c;
			edge[i*2+1].next=head[b];
			head[b]=i*2+1;
		}
		kp=1;
		bfs(n,-1,0);
		DFS(n,-1,1);
	/*	for (int i=1;i<=n;i++)
		printf("%4d",i);printf("\n");
		for (int i=1;i<=n;i++)
		printf("%4d",R[i]);printf("\n");
		for (int i=1;i<kp;i++)
		printf("%4d",i);printf("\n");*/
		for (int i=1;i<kp;i++)
		{
	//		printf("%4d",vis[i]);
			QM[i][0]=i;
		}
	/*	printf("\n");
		for (int i=1;i<kp;i++)
			printf("%4d",D[i]);
			printf("\n");*/
		int nn=kp-1;
		int p=log2(nn);
		for (int i=1;i<=p;i++)
		for (int j=1;j+(1<<i)-1<=nn;j++)
		{
			int aa=QM[j][i-1];
			int bb=QM[j+(1<<(i-1))][i-1];
	//		printf("%d   %d   %d   %d 66666666666\n",aa,bb,D[aa],D[bb]);
			if (D[aa]<=D[bb])
				QM[j][i]=aa;
			else
				QM[j][i]=bb;
	//		printf("%d  %d  %d\n",j,i,QM[j][i]); 
		}
	/*	printf("\n");
		for (int i=1;i<=n;i++)
		printf("%4d\n",dis[i]);
	*/	while (m--)
		{
			scanf("%d%d",&a,&b);
			int aa=min(R[a],R[b]);
			int bb=max(R[a],R[b]);
	//		printf("%d   %d     9999999999999\n",aa,bb);
			int pp=log2(bb+1-aa);
			int aaa=QM[aa][pp];
			int bbb=QM[bb+1-(1<<pp)][pp];
	   //     c=min(QM[aa][pp],QM[bb+1-(1<<pp)][pp]); 
	//  		printf("%d   %d    666\n",aaa,bbb);
	     	if (D[aaa]<=D[bbb])
	     	c=aaa;
	     	else
	     	c=bbb;
	//	    printf("%d\n",c);
		    c=vis[c]; 
	    //    printf("ans    :   %d\n",dis[a]+dis[b]-2*dis[c]);
			printf("%d\n",dis[a]+dis[b]-2*dis[c]);
		}
	}
	return 0;
}
/*2
13 13
1 2  1
1 3  1
1 4  1
3 5  1
3 6  1
3 7  1
6 8  1
6 9  1
7 10  1
7 11  1
10 12  1
10 13  1*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值