POJ 1984 Navigation Nightmare(带权并查集)

题目链接:http://poj.org/problem?id=1984

题目大意,一开始给出一个n和m,分别代表一共有n个节点,m次操作,每次操作花费一秒,按照顺序执行。然后有k次询问,询问有三个变量f1,f2,i,问f1和f2的哈密顿距离是多少。哈密顿距离就是两个点坐标各自相减取绝对值,|x1 - x2|+ |y1 - y2|。样例就是题目上面那个图。i是询问第几秒时候他们是否联通,若联通则输出距离。

这个题目,我从星期一开始做,做到今天星期五终于做出来了,好久没有试过磨出来一道题了,习惯看题解了,这次我觉得我能做就一直没看题解。后来我也真的是前前后后wa了20多次的样子,还找到了他的测试数据,发现自己好多东西都不对。一直改一直改,今天晚上,重新审视一下我的代码,突然灵光一现,重新制定了计划,去掉了很多东西,就过了,还不错。

说说我最后的想法,修改多次了!!!

这个题目有东南西北,我们只需要x和y去存就行了,读入到E就是+x的方向嘛,N就是+y的方向嘛。带权并查集订好了距离其实本质就是并查集。

并查集就不多讲了,然后现在就是要想怎么把距离也更新。

1,如果有这样一个关系,a和b要合并,他们的根分别是c和d,那么我永远都规定后面那个根连到前面那个根上面。但是根,本来他的x和y肯定是(0,0)的,那么我先从d走返回去b那,说白了就是d.x += -b.x , d.y += -b.y,这时候,我要连接到c上面,那么我先根据他们之间的关系,NSEW什么的,又再次加到d.x , d.y上面,这时候我们知道a.x, a.y,再把这个加到d.x, d.y上面,完美,这不就是d到c的距离更新完了吗?然后再来一句,par[d] = c,搞定。

2,这样据说是可以过的,但是我觉得还不够,我决定还要路径压缩一下,这里的路径压缩,有点意思,我也是想了好几种方案才相出这种递归压缩的方法。跟以前的不同的就是,比如a连着b,b连着c,c连着d,d是根,我们要a连接到d上,应该是从a一直循环下去,记住,必须是循环。a.x += b.x, a.y += b.y,然后a.x += c.x, a.y += c.y,这样我们就把a直接连接到d上面了。接下来,用一个递归,par[a] = Find(par[a]),这样一进去,又执行b的更新操作,Find函数的返回值是查找元素的根。

3,我们要看看这个查询时候的时间问题了,我们不可能把每一时刻每个点的状态都存下来,否则就要有5W * 5W的复杂度了。明智的做法就是,把这些数据全部先存起来,然后再一边建图一边查询。

代码如下:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<math.h>
#include<stdlib.h> 
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 40000 + 5;
int par[40005], disx[40005], disy[40005];
int a[MAXN], b[MAXN], v[MAXN], str[MAXN];

struct node
{
	char ch;
	int point, val;
};

struct ti
{
	int fa,fb,t,id;
	int val;
}f[MAXN];

bool cmp(ti a, ti b)
{
	return a.t < b.t;
}

bool cmp2(ti a, ti b)
{
	return a.id < b.id;
}

void init()
{
	memset(disx, 0, sizeof(disx));
	memset(disy, 0, sizeof(disy));
	for(int i = 0; i < 40005; i++)
		par[i] = i;
}

int Find(int x)
{
	if(par[x] == x)
		return x;
	int p = x;
	while(par[p] != p)
	{
		disx[x] += disx[par[p]];
		disy[x] += disy[par[p]];
		p = par[p];
	}
	
	par[x] = Find(par[x]);
	return par[x];
}

int Find_dis(int a, int b)
{
	int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
	int x;
	x = a;
	while(par[x] != x)
	{
		x1 += disx[x];
		y1 += disy[x];
		x = par[x];
	}
	x = b;
	while(par[x] != x)
	{
		x2 += disx[x];
		y2 += disy[x];
		x = par[x];
	}
	return abs(x1 - x2) + abs(y1 - y2);
}

void Union(int a, int b, int x, int y, int v, char ch)
{
	node tmp;
	disx[y] = -disx[b] + disx[a];
	disy[y] = -disy[b] + disy[a];
	
	if(ch == 'N')
		disy[y] -= v;
	else if(ch == 'E')
		disx[y] -= v;
	else if(ch == 'S')
		disy[y] += v;
	else if(ch == 'W')
		disx[y] += v;

	par[y] = x;
}

int main()
{
	char ch;
	int n, m, ppp, k;
	while(cin >> n >> m)
	{
		init();
		ppp = 1;
		for(int i = 1; i <= m; i++)
			scanf("%d %d %d %c", &a[i], &b[i], &v[i], &str[i]);
		cin >> k;
		for(int i = 1; i <= k; i++)
		{
			scanf("%d%d%d", &f[i].fa, &f[i].fb, &f[i].t);
			f[i].id = i;
		}
		sort(f + 1, f + k + 1, cmp);
		
		for(int i = 1; i <= m; i++)
		{
			int x = Find(a[i]);
			int y = Find(b[i]);
			if(x != y)
				Union(a[i], b[i], x, y, v[i], str[i]);
			while(ppp <= k && f[ppp].t == i)
			{
				if(Find(f[ppp].fb) == Find(f[ppp].fa))
					f[ppp].val = Find_dis(f[ppp].fa, f[ppp].fb);
				else
					f[ppp].val = -1;
				ppp++;
			}	
		}	
		sort(f + 1, f + k + 1, cmp2);
		for(int i = 1; i <= k; i++)
			printf("%d\n", f[i].val);
	}
	return 0;
 } 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值