题意:给一个M*N的矩形区域,有M*N个方格,有些方格为空(可到达),有些非空(不可达)。现A和B在博弈,他们任选两个不同的空格,站在各自的格子中央,A可以移动,但只能进行一次行方向或列向方移动,移动后仍然在格子中央。A如果移动到一个位置使得B看不见他,则A获胜。B看不见A的条件是存在一个非空格子与B到A的线段相切或相交。问,对于每个空格子,A站在上面,是否无论B在哪里,他都可以移动到一个安全位置。
A可以选择不移动,题目保证至少有两个空格子,每次移动只能进行横向或竖向移动,不能都进行。空格子内部全空,非空格子内部和边界非空。M,N 不超过30
链接:http://codeforces.com/gym/100496 J题
解法:由于M,N 不超过30,可以考虑暴力做。假如我们已经知道站在任意两个空格子上能否相互看到,下面就好做了。
一,如何知道站在任意两个空格子上能否相互看到
暴力枚举任意两个空格子,判断他们中心点连线的线段,是否被一个非空格子挡住,这里需要优化,如果暴力枚举每个非空格子,效率比较低,不如枚举以两个空格子为顶点的小矩形的每一列,并且每次只访问非空格子。这里需要预处理一个函数,表示每个格子下面第一个非空格子在哪。
二,知道了后怎么办
知道了后,枚举每个空格子,假设B在它上面,再暴力枚举每个空格子,假设A终点位置在其上面,如果A和B被挡住了,则表示A所在的空格子是安全位置,那么能到达它的位置都是安全的,除了B所在位置以外(因为A和B不可选同一个格子)。记录下对于每个B所在的空格子,每个空格子是否是安全的,最后判断每个空格子安全指数是否是空格子个数减1即可。
小结:说了那么多,这题用到几何的部分,只有线段与线段相交,这里的相交指广义上的,即只要有交点就算相交。判线段相交的函数如下:
struct Seg{
Point p1, p2;
Seg(){};
Seg(Point p11, Point p22){p1 = p11, p2 = p22;}
};
bool PointOnSeg(Point p, Seg s){
if((s.p1 - p) / (s.p2 - p)) return false;
else if(sgn((s.p1 - p) * (s.p2 - p)) > 0) return false;
else return true;
}
bool operator / (const Seg a, const Seg b){//此函数容易写错,且不同题目需要修改,特别注意
if((a.p2 - a.p1) || (b.p2 - b.p1)){
return PointOnSeg(a.p1, b) || PointOnSeg(a.p2, b) ||
PointOnSeg(b.p1, a) || PointOnSeg(b.p2, a);
}
else return sgn((a.p2 - a.p1) % (b.p1 - a.p1)) * sgn((a.p2 - a.p1) % (b.p2 - a.p1)) <= 0 &&
sgn((b.p2 - b.p1) % (a.p1 - b.p1)) * sgn((b.p2 - b.p1) % (a.p2 - b.p1)) <= 0 ;
}
此函数经过反复修改,这个方法是一个准确、效率较高的方法。但注意,不同题目线段相交的定义可能不同,比如有时不允许交点在端点,这个时候函数需要自行改动。而判断线段与直线相交,射线和线段相交等,也是在此函数拓展而来的。
此题代码
//Hello. I'm Peter.
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const double eps = 1e-9, pi = acos(-1.0);
inline int sgn(double x){
if(fabs(x) < eps) return 0;
else return x > 0? 1 : -1;
}
struct Point{
double x, y;
Point(){};
Point(double x1, double y1){x = x1, y = y1;}
};
typedef Point Vector;
Vector operator + (const Vector a, const Vector b){return Vector(a.x + b.x, a.y + b.y);}
Vector operator - (const Vector a, const Vector b){return Vector(a.x - b.x, a.y - b.y);}
double operator * (const Vector a, const Vector b){return a.x * b.x + a.y * b.y;}
double operator % (const Vector a, const Vector b){return a.x * b.y - a.y * b.x;}
Vector operator * (const Vector a, const double b){return Vector(a.x * b, a.y * b);}
Vector operator * (const double b, const Vector a){return Vector(a.x * b, a.y * b);}
struct Seg{
Point p1, p2;
Seg(){};
Seg(Point p11, Point p22){p1 = p11, p2 = p22;}
};
bool operator / (const Seg a, const Seg b){//need change
return sgn((a.p2 - a.p1) % (b.p1 - a.p1)) * sgn((a.p2 - a.p1) % (b.p2 - a.p1)) <= 0 &&
sgn((b.p2 - b.p1) % (a.p1 - b.p1)) * sgn((b.p2 - b.p1) % (a.p2 - b.p1)) <= 0 ;
}
int m, n;
char grid[50][50];
int val[50][50];
struct Downto{
int x, y;
}downto[50][50];
void Build_downto(){
for(int j = 1; j <= n; j++){
for(int i = m; i >= 1; i--){
if(grid[i][j] == '#') downto[i][j].x = i,downto[i][j].y = j;
else{
if(i == m) downto[i][j].x = m + 1, downto[i][j].y = j;
else downto[i][j] = downto[i+1][j];
}
}
}
}
struct Maze{
Point p[6];
int i, j;
}maze[50][50];
void Build_Maze(){
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
maze[i][j].i = i;
maze[i][j].j = j;
}
}
maze[1][1].p[0] = Point(1, -1);
maze[1][1].p[1] = Point(0, 0);
maze[1][1].p[2] = Point(2, 0);
maze[1][1].p[3] = Point(2, -2);
maze[1][1].p[4] = Point(0, -2);
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
if(i == 1 && j == 1) continue;
if(j == 1){
for(int k = 0; k <= 4; k++){
maze[i][j].p[k] = maze[i-1][j].p[k] + Vector(0, -2);
}
}
else{
for(int k = 0; k <= 4; k++){
maze[i][j].p[k] = maze[i][j-1].p[k] + Vector(2, 0);
}
}
}
}
}
bool dangzhu[31][31][31][31];
bool dang(Point p1, Point p2, int minx, int maxx,int miny,int maxy){
for(int j = miny; j <= maxy; j++){
int xt = minx, yt = j;
int x, y;
x = downto[xt][yt].x;
y = downto[xt][yt].y;
while(x <= maxx){
for(int k = 1; k <= 4; k++){
int nexk = k == 4 ? 1: k + 1;
if(Seg(maze[x][y].p[k], maze[x][y].p[nexk]) / Seg(p1 ,p2)){
return true;
}
}
x++;
if(x > maxx) break;
xt = x, yt = y;
x = downto[xt][yt].x;
y = downto[xt][yt].y;
}
}
return false;
}
void Build_dangzhu(){
for(int i1 = 1; i1 <= m; i1++){
for(int j1 = 1; j1 <= n; j1++){
if(grid[i1][j1] == '#') continue;
for(int i2 = i1; i2 <= m; i2++){
for(int j2 = 1; j2 <= n; j2++){
if(i2 == i1 && j2 <= j1) continue;
if(grid[i2][j2] == '#') continue;
if(dang(maze[i1][j1].p[0], maze[i2][j2].p[0], min(i1,i2), max(i1,i2), min(j1,j2), max(j1,j2))){
dangzhu[i1][j1][i2][j2] = true;
dangzhu[i2][j2][i1][j1] = true;
}
}
}
}
}
}
int vis[50][50],kase;
void go(int initi, int initj){
if(vis[initi][initj] != kase) vis[initi][initj] = kase, val[initi][initj] += 1;
for(int i = initi - 1; i >=1 ;i--){
if(grid[i][initj] == '#') break;
if(vis[i][initj] != kase) vis[i][initj] = kase, val[i][initj] += 1;
}
for(int i = initi + 1; i <= m ;i++){
if(grid[i][initj] == '#') break;
if(vis[i][initj] != kase) vis[i][initj] = kase, val[i][initj] += 1;
}
for(int j = initj - 1; j >=1 ;j--){
if(grid[initi][j] == '#') break;
if(vis[initi][j] != kase) vis[initi][j] = kase, val[initi][j] += 1;
}
for(int j = initj + 1; j <= n ;j++){
if(grid[initi][j] == '#') break;
if(vis[initi][j] != kase) vis[initi][j] = kase, val[initi][j] += 1;
}
}
struct Answer{
int i, j;
}ans[100010];
int numans;
bool cmpans(const Answer a, const Answer b){
if(a.i != b.i) return a.i < b.i;
else return a.j < b.j;
}
int main(){
freopen("jealous.in","r",stdin);
freopen("jealous.out","w",stdout);
scanf("%d%d",&m,&n);
for(int i = 1; i <= m; i++){
scanf("%s",grid[i] + 1);
}
Build_Maze();
Build_downto();
Build_dangzhu();
kase = 0;
for(int i1 = 1; i1 <= m; i1++){
for(int j1 = 1; j1 <= n; j1++){
if(grid[i1][j1] == '#') continue;
kase++;
for(int i2 = 1; i2 <= m; i2++){
for(int j2 = 1; j2 <= n; j2++){
if(grid[i2][j2] == '#') continue;
if(dangzhu[i1][j1][i2][j2]){
go(i2, j2);
}
}
}
if(vis[i1][j1] == kase) val[i1][j1] --;
}
}
int tot = 0;
for(int i = 1; i <= m; i++){
for(int j = 1; j<= n; j++){
if(grid[i][j] == '.') tot++;
}
}
for(int i = 1; i <= m; i++){
for(int j = 1; j<= n; j++){
if(grid[i][j] == '.' && val[i][j] == tot - 1){
int t = ++numans;
ans[t].i = i;
ans[t].j = j;
}
}
}
if(numans > 0) sort(ans + 1, ans + 1 + numans, cmpans);
printf("%d\n",numans);
for(int i = 1; i <= numans; i++){
printf("%d %d\n",ans[i].i, ans[i].j);
}
return 0;
}