TYVJ 2009「Poetize8」Lagoon

复杂图案扩散模拟
本文介绍了一个基于特定图案的扩散模拟问题。通过BFS算法遍历并记录不同图案块的扩散情况,实现对任意指定坐标倾倒虚拟污水后,计算其扩散面积的方法。涉及算法设计、数据结构使用及具体实现细节。

描述

忘川沧月的小水塘的水面是一片由以下两种图形组成的图案:
 <图lagoon-1>
这两种图形都由一个边长为2的正方形和两个半径为1的1/4圆组成,圆心在正方形的两个对角顶点上。
小水塘左上角坐标为(0,0),右下角坐标为(2*n,2*m)。水面上每一个顶点坐标为偶数的2*2正方形,都是上面两个图形中的一种。如果我们往水塘中的某个位置倒一桶污水,那么这桶污水会像画图中的油漆桶一样扩散开来,占据一片连续的区域,但是在线条边界处会停止扩散。注意如果倒在了线条上,扩散面积为0。
如下图所示,就是一个由4行4列上述图形组成的、左上角坐标(0,0)、右下角坐标(8,8)、某些位置倒了污水的水塘(白色部分为线条边界,线条实际宽度认为是0,为了明显、美观,此处加粗显示):
 <图lagoon-2>
现在给出一个n行m列的由上述两种图形组成的水塘,起初水塘中全部为净水。给定q个往某个坐标上(x,y)倾倒污水的操作,对于每次操作,请求出在(x,y)上倾倒的污水最终会扩散多大的面积。


输入格式

第一行两个整数n、m。
接下来n行每行m个整数,每个整数是0或者1,中间没有空格隔开。0代表此处是<图lagoon-1>中左侧那个2*2的图形,1代表此处是右侧那个图形。例如<图lagoon-2>中的第一行用“0001”表示。
第n+2行是一个整数q。
接下来q行每行两个用空格隔开的整数x、y,表示这一次倾倒污水的坐标。


输出格式

对于每个询问,输出此次倾倒的污水最终扩散的面积,四舍五入保留4位小数。

测试样例1

输入

样例输入1
1 2
01
4
0 0
2 0
0 1
0 2

样例输入2
3 1
1
0
1
2
3 1
4 2

输出

样例输出1
0.7854
4.8584
0.0000
4.8584

样例输出2
7.2876
1.5708

备注

对于 100% 的数据,1<=n,m,q<=100,0<=x<=2*n,0<=y<=2*m。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

BFS遍历一遍,要用数组记录可能的情况。

思路说起来很简单,但是情况好多写起来好麻烦的。

我的写了180行······丑到不能看······写完的时候才想起来可以用数组记录······好方······

(下面是神犇GZZ的代码)

#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#include <vector>
using std::queue;
using std::vector;

const int maxnm=100+5;
int map[maxnm][maxnm];

struct Size {
	int x;	//1*1正方形个数 
	int y;	//1/4圆个数 
	Size(int x, int y): x(x), y(y) {}
	inline Size operator +(const Size &b) const
	{
		Size r(x+b.x, y+b.y);
		return r;
	}
	inline double get()
	{
#ifdef TEST
		printf("{%d, %d}.get()!\n", x, y);
#endif
		const double pi=acos(-1.0);
		return x*1.0+y*pi*0.25;
	}
};

Size dw[3]={Size(0, 1), Size(4, -2), Size(0, 1)};
bool vis[maxnm][maxnm][3];	//某地砖某位置是否被vis了...QAQ 
struct Pos {
	int x, y;	//所在地砖坐标
	int t;		//所在位置类型 
	Pos(int x, int y, int t): x(x), y(y), t(t) { } 
};

int pace[4][2]={{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
int turn[2][4][2][3]={
	{//0->...
		//0
		{{1, 2, -1}, {0, 1, -1}}, 
		//1
		{{1, 2, -1}, {2, 1, -1}}, 
		//2
		{{-1, 0, 1}, {-1, 1, 2}}, 
		//3
		{{-1, 0, 1}, {-1, 1, 0}}
	},
	{//1->...
		//0
		{{-1, 1, 2}, {-1, 0, 1}},
		//1
		{{2, 1, -1}, {1, 2, -1}},
		//2
		{{0, 1, -1}, {1, 2, -1}},
		//3
		{{-1, 1, 0}, {-1, 0, 1}}
	}
};
int have[2][4]= {
	{0, 1, 1, 2},
	{1, 2, 0, 1}
};

int n, m;

inline Size bfs(const vector<Pos> &st)
{
	memset(vis, 0, sizeof(vis));
	Size ans(0, 0);
	queue<Pos> q;
	for(int i=0; i<(signed int)st.size(); ++i)
	{
		if(st[i].x<1 || st[i].x>n || st[i].y<1 || st[i].y>m) continue;
		q.push(st[i]);
		vis[st[i].x][st[i].y][st[i].t]=true;
		ans=ans+dw[st[i].t];
	}
	while(!q.empty())
	{
		Pos p=q.front(); q.pop();
#ifdef TEST
		printf("getting in (%d, %d) type %d!\n", p.x, p.y, p.t);
#endif
		for(int k=0; k<4; ++k)
		{
			int x1=p.x+pace[k][0], y1=p.y+pace[k][1];
			if(x1<1 || x1>n || y1<1 || y1>m) continue;
			int tu=turn[map[p.x][p.y]][k][map[x1][y1]][p.t];
			if(tu==-1 || vis[x1][y1][tu]) continue;
			ans=ans+dw[tu];
			vis[x1][y1][tu]=true;
			q.push(Pos(x1, y1, tu));
		}
	}
	return ans;
}

char t_str[maxnm];

int main()
{
	//freopen("flooding.in", "r", stdin);
	//freopen("flooding.out", "w", stdout);
	scanf("%d%d", &n, &m);
	for(int i=1; i<=n; ++i)
	{
		scanf("%s", t_str+1);
		for(int j=1; j<=m; ++j) map[i][j]=t_str[j]-'0';
	}
	int q; scanf("%d", &q);
	while(q--)
	{
		int x, y; scanf("%d%d", &x, &y);
		vector<Pos> v;
		if((x&1) && (y&1))
		{
			v.push_back(Pos((x>>1)+1, (y>>1)+1, 1));
		}
		else if((x&1) || (y&1))
		{
			puts("0.0000");
			continue;
		}
		else
		{
			v.push_back(Pos(x>>1, y>>1, have[map[x>>1][y>>1]][3]));
			v.push_back(Pos(x>>1, (y>>1)+1, have[map[x>>1][(y>>1)+1]][2]));
			v.push_back(Pos((x>>1)+1, y>>1, have[map[(x>>1)+1][y>>1]][1]));
			v.push_back(Pos((x>>1)+1, (y>>1)+1, have[map[(x>>1)+1][(y>>1)+1]][0]));
		}
		printf("%.4f\n", bfs(v).get());
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值