【xdoj难题集】1089玩水珠的V8

刚刚历尽千辛万苦,终于搞定了,还是那个问题,细节细节细节。可能模拟还是做得太少,希望以后能好点。再加上这道题我的算法出奇得快,所以还是值得一提的。


先说说大体的感想,看到这道题就知道又是一道模拟,刚开始觉得有点难,不过看了之后觉得其实简单了很多,首先其实只有两个过程,分裂和结合,所以要在这些操作上下文章,至于操作的时间先后的问题,我的想法肯定是优先队列,用这个队列记录一种操作的过程,然后再把潜在的新操作记录下来。

再说说具体的吧。
1第一步是输入输出,把各个大水珠点的坐标记录,并且在一个数组中记录这些点的大小,0的点代表没有,因为有可能需要记录裂开的时间我又比较懒,所以就灵机一动用负数代表时间,这样最后确认的时候也很方便,只要看看数组的正负即可。

2然后就是搜索了,这个地方的确有很多细节,我记录的是小水珠的添加,也就是在一个时间和一个坐标(当然之后还加入了一个方向),表示这个时间天降水珠在这个地方,坐标保证是大水珠(当然之后发现有极少可能会不是),这样就先确认一下时间,如果超时了就直接break,之后进行添加并判断是否分裂,分裂就把这个地方的数组变成时间的相反数,然后再往四个方向进行搜索,如果发现有大水珠就把那个水珠的坐标以及小水珠到达的时间添加进队列中。
3这样看着很美,但wa了不少次,想了很久才发现一个漏洞,那就是有可能这个地方不再有大水珠了,也就是有这个地方的值为负数的情况,这时候就要考察一下了,首先如果有多个水珠同时到达这个地方,那就有可能在之前就爆炸过了,所以要跳过,这时候判断一下爆炸事件和操作事件是否相等即可,但是还有另一种情况,就是你这个分裂时候的操作和到达的时候是有时间差的,如果这段时间这个大水珠被炸掉了那你看到的也只剩下一片焦痕,这个就搞不定了,不过幸好大水珠不会凭空出现,只会减少,所以遇到这种情况只要继续搜索就行了,那么这个时候就需要添加一项,那就是转移的方向,所以加入了这个机制后就ac了,这个算法还是挺快的,相比于一步一步的移动少了很多出队入队的问题。
# include <stdio.h>
# include <string.h>
# include <algorithm>
# include <queue>

using namespace std;

const int MAX_N = 105;

typedef pair<int , int> P;
typedef pair<P , P> W;

int dx[4] = {1 , 0 , -1 , 0}, dy[4] = {0 , 1 , 0 , -1};
int N, M, K, T;
int box[MAX_N][MAX_N];
int sx, sy;

P wat[MAX_N];

void sea()
{
	priority_queue<W , vector<W> , greater<W> > que;
	
	que.push(W(P(0 , 0) , P(sx , sy)));
	
	int i;
	while(!que.empty())
	{
		W y = que.top();
		que.pop();
		int ex = y.second.first, ey = y.second.second, now = y.first.first, pos = y.first.second;
		
		if(now > T)
			break;
		
		if(box[ex][ey] < 0)
		{
			if(-box[ex][ey] == now)
				continue; 
			
			int px = ex + dx[pos], py = ey + dy[pos], k = 1;
			while(px > 0 && px <= N && py > 0 && py <= M)
			{
				if(box[px][py] > 0)
				{
					que.push(W(P(now + k , pos), P(px , py)));
					break;
				}
				k++;
				px += dx[pos];
				py += dy[pos];
			}
			
			continue;
		}
		
		box[ex][ey]++;
		
		if(box[ex][ey] > 4)
		{
			box[ex][ey] = -now;
			for(i = 0 ; i < 4 ; i++)
			{
				int nx = ex + dx[i], ny = ey + dy[i], j = 1;
				while(nx > 0 && nx <= N && ny > 0 && ny <= M)
				{
					if(box[nx][ny] > 0)
					{
						que.push(W(P(now + j , i), P(nx , ny)));
						break;
					}
					j++;
					nx += dx[i];
					ny += dy[i];
				}
			}
		}
	}
}

int main()
{
	while(~scanf("%d %d %d %d", &N, &M, &K, &T))
	{
		memset(box , 0 , sizeof(box));
		
		int i, t, s, y;
		for(i = 0 ; i < K ; i++)
		{
			scanf("%d %d %d", &t, &s, &y);
			
			box[t][s] = y;
			wat[i].first = t;
			wat[i].second = s;
		}
		
		scanf("%d %d", &sx, &sy);
		box[sx][sy] = 4;
		sea();
		
		for(i = 0 ; i < K ; i++)
		{
			if(box[wat[i].first][wat[i].second] > 0)
				printf("1 %d\n", box[wat[i].first][wat[i].second]);
			else
				printf("0 %d\n", -box[wat[i].first][wat[i].second]);
		}
	}
	
	return 0;
}

最后说一下这个pair,因为犯懒所以直接用的一大堆pair的叠加,不过因为pair有大小(先比前面再比后面)所以这样是没有问题的,这个小技巧还是挺有用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值