1. 程式人生 > >【CZY選講·棋盤迷宮】

【CZY選講·棋盤迷宮】

ash true 數據 scanf 時間 分治 b-s ron 分開

題目描述

一個N*M的棋盤,’.’表示可以通過,’#’表示不能通過,給出Q個詢問,給定起點和終點,判斷兩點是否聯通,如聯通輸出“Yes”,否則輸出“No”。?

數據範圍

N,M <=500,Q <=10^6。

題解:

①由於存在兩個方向和不可逆性,標記聯通分量的方法不可行

②分治算法。按照行將棋盤一分為二,進行DP處理:

用f[i][j]表示點(i,j)與中線上每個點的聯通性,可用bitset壓位處理。?

轉移方程式:f[i][j]=f[i][j+1]|f[i+1][j]

③離線詢問,將詢問隨著分治區間而分開處理。

④時間復雜度: O(n2*logn)

#include <cstdio>
#include <vector>
#include <bitset>
using std::vector;
using std::bitset;
const int QUERY_SIZE = 600006;
const int MAP_SIZE = 511;

int N, M, Q;
char map[MAP_SIZE][MAP_SIZE];
int ans[QUERY_SIZE];
bitset<MAP_SIZE> f[MAP_SIZE][MAP_SIZE], g[MAP_SIZE][MAP_SIZE];
struct query {
	int x1, y1, x2, y2, id;
};

query q;
void solve(vector<query> v, int l, int r) {
	int m = (l + r) >> 1;
	if (l > r) return ;
	for (int i = m; i >= l; i--)
		for (int j = M; j >= 1; j--) {
			f[i][j] = 0;
			if (map[i][j] == ‘.‘) {
				if (i == m) f[i][j].set(j);
				else f[i][j] |= f[i + 1][j];
				if (j != M) f[i][j] |= f[i][j + 1];
			}
		}
	for (int i = m; i <= r; i++)
		for (int j = 1; j <= M; j++) {
			g[i][j] = 0;
			if (map[i][j] == ‘.‘) {
				if (i == m) g[i][j].set(j);
				else g[i][j] |= g[i - 1][j];
				if (j != 1) g[i][j] |= g[i][j - 1];
			}
		}
	vector<query> vl, vr;
	for (vector<query>::iterator it = v.begin(); it != v.end(); it++) {
		q = *it;
		if (q.x2 < m) vl.push_back(q);
		else if (q.x1 > m) vr.push_back(q);
		else ans[q.id] = (f[q.x1][q.y1] & g[q.x2][q.y2]).any();
	}
	solve(vl, l, m - 1);
	solve(vr, m + 1, r);
}

int main() {
	freopen("boardgame.in", "r", stdin);
	freopen("boardgame.out", "w", stdout);
	scanf("%d %d", &N, &M);
	for (int i = 1; i <= N; i++)
		scanf("%s", map[i] + 1);
	vector<query> v;
	scanf("%d", &Q);
	for (int i = 0; i < Q; i++) {
		scanf("%d %d %d %d", &q.x1, &q.y1, &q.x2, &q.y2);
		q.id = i;
		v.push_back(q);
	}
	solve(v, 1, N);
	for (int i = 0; i < Q; i++)
		puts(ans[i] ? "Yes" : "No");
	return 0;
}//czy020202

那神秘的光芒像暴風雨般凜冽著,大地在無情的追問中幻滅成挽歌,

如夢的迷霧隨著詩篇消逝在遠山…… ————————————汪峰《信仰在空中飄揚》

【CZY選講·棋盤迷宮】