【4连WA】【Usaco Oct08 Gold】奶牛串门(Pasture Walking)

本文解析USACO Gold级别的“奶牛串门”问题,介绍如何使用倍增算法解决路径查找问题,避免常见错误并提供高效实现代码。

【Usaco Oct08 Gold】奶牛串门(Pasture Walking)

Time Limit:30000MS  Memory Limit:65536K
Total Submit:75 Accepted:50 
Case Time Limit:3000MS

Description

有N (2 <= N <= 1,000)头奶牛,分别编号为1到N。还有N颗牧草分别编号为1到N。简单起见,第i头奶牛都盯着第i颗牧草。 

有几对牧草分别用一些小路连接了起来,总共有N-1条双向的小路,小路i连接了Ai及Bi颗牧草(1 <= Ai <= N; 1 <= Bi <= N),小路的长度为Li (1 <= Li <= 10,000)保证任意两颗牧草总能通过小路走到,也就是说这些小路构成了一棵树。 

奶牛是群居性的动物,很喜欢串门。奶牛们会问你Q次问题(1 <= Q <= 1,000),每次询问的内容很简单,就是从p1颗牧草到p2颗牧草最短的距离是多少。(1 <= p1 <= N; 1 <=p2 <= N)

Input

第一行,两个用空格分隔的整数:N和Q 
第二行到第N行,第i+1行包含三个用空格分隔的整数Ai,Bi,Li 
第N+1行到第N+Q行,每行包含两个用空格分隔的整数p1,p2

Output

共Q行,每行包含每次询问的最短距离的值。

Sample Input

4 2
2 1 2
4 3 2
1 4 3
1 2
3 2

Sample Output

2
7

Hint

样例解释: 
询问1:1->2 总代价为2 
询问2:3->4->1->2 总代价为7

Source

Usaco October 2008 Gold


这本来算是一道裸题的,但是由于我对倍增算法不熟悉,犯了以下一个傻逼的错误,导致了惨痛的4连WA:

len[x][i]记录的是x节点向上走(1<<i)步的权值,而我犯错误的是,我把向上走(1<<i)写成了len[x][fa[x][i]],只有呵呵了。

然后读了416162623 的代码我才知道自己错在哪,呃,还有就是相邻的点的权值不一定要用n2的复杂度来记录,416162623是存在边里边的,空间小速度快,我这个就相形见绌了。


416162623的代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
using namespace std;
int n,q;
int cnt=0;
int f[1005][50];
int M[1005][50];
int depth[10005];
bool mark[10005];
int last[10005],next[10005],end[10005],len[10005];
void dfs(int x)
{
    mark[x]=true;
	int a,b,c,d,e;
	depth[x]=depth[f[x][0]]+1;
	a=ceil(log(depth[x])/log(2));
	for(b=1;b<=a;b++)
	{
	f[x][b]=f[f[x][b-1]][b-1];
	M[x][b]=M[x][b-1]+M[f[x][b-1]][b-1];
    }
    b=last[x];
	while(b!=0)
	{
		if(mark[end[b]]==false)
		{
		f[end[b]][0]=x;
		M[end[b]][0]=len[b];
		dfs(end[b]);
	    }
		b=next[b];
	}
}
int LCA(int x,int y)
{
	
	int a,b,c,d,e;
	int ans=0;
	if(depth[x]>depth[y]){a=x;x=y;y=a;}
	e=ceil(log(n)/log(2));
	int k=depth[y]-depth[x];
	for(a=0;a<=e;a++)
	if(k&(1<<a))
	{
		ans+=M[y][a];
        y=f[y][a];
    }
	if(x==y)return ans;
	e=ceil(log(depth[x])/log(2));
	for(a=e;a>=0;a--)
	if(f[x][a]!=f[y][a])
	{
	ans+=M[x][a];
	x=f[x][a];
	ans+=M[y][a];
	y=f[y][a];
    }
    ans+=M[x][0];
    ans+=M[y][0];
    return ans;
}
int main()
{
	int a,b,c,d,e;
	scanf("%d%d",&n,&q);
	for(a=1;a<=n-1;a++)
	{
		scanf("%d%d%d",&b,&c,&d);
		cnt++;
		end[cnt]=c;
		len[cnt]=d;
		next[cnt]=last[b];
		last[b]=cnt;
		cnt++;
		end[cnt]=b;
		len[cnt]=d;
		next[cnt]=last[c];
		last[c]=cnt;
	}
	dfs(1);
	for(a=1;a<=q;a++)
	{
		scanf("%d%d",&b,&c);
		printf("%d\n",LCA(b,c));
	}
	return 0;
}


我的大空间复杂度代码

#include<cstdio>
#include<vector>
#include<cmath>
using namespace std;
#define maxn 1009
int dep[maxn], fa[maxn][13], len[maxn][13], zlen[maxn][maxn];
bool wasfa[maxn];
vector<int>edge[maxn];
int BigS;
void dfs(int cur)
{
	int k, i, son;
	dep[cur]=dep[fa[cur][0]]+1;
	k=ceil(log(dep[cur])/log(2));
	for(i=1; i<=k; i++)
	{
		fa[cur][i]=fa[fa[cur][i-1]][i-1];
		len[cur][i]=len[cur][i-1]+len[fa[cur][i-1]][i-1];
		//原来错写成len[cur][i]=len[cur][fa[cur][i-1]]+...概念理解错误 
	}
	wasfa[cur]=1;
	vector<int>::iterator it;
	for(it=edge[cur].begin(); it!=edge[cur].end(); it++)
	{
		son=(*it);
		if(wasfa[son]==0)
		{
			fa[son][0]=cur;
			len[son][0]=zlen[son][cur];
			dfs(son);
		}
	}
}
int lca(int x, int y)
{
	int i, k, s, ans=0;
	if(dep[x]<dep[y])	swap(x, y);
	k=dep[x]-dep[y];
	for(i=0; i<=BigS; i++)
	{
		if(k&(1<<i))
		{
			ans+=len[x][i];
			x=fa[x][i];
		}
	}
	if(x==y)	return ans;
	
	s=ceil(log(dep[x])/log(2));
	for(i=s; i>=0; i--)
	{
		if(fa[x][i]!=fa[y][i])
		{
			ans+=len[x][i];
			x=fa[x][i];
			ans+=len[y][i];
			y=fa[y][i];
		}
	}
	return ans+len[x][0]+len[y][0];
}
int main()
{
	int n, q, i, a, b, l, p1, p2;
	scanf("%d%d", &n, &q);
	BigS=ceil(log(n)/log(2));
	for(i=1; i<n; i++)
	{
		scanf("%d%d%d", &a, &b, &l);
		zlen[a][b]=l;
		zlen[b][a]=l;
		edge[a].push_back(b);
		edge[b].push_back(a);
	}
	dfs(1);
	for(i=1; i<=q; i++)
	{
		scanf("%d%d", &p1, &p2);
		printf("%d\n", lca(p1, p2));
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值