题意
n ∗ m n*m n∗m 大小的方格图,起点 ( 1 , 1 ) (1,1) (1,1) ,给定 k k k 个障碍物,每次只能直行或右拐,初始方向为右,问是否能走遍除障碍物之外的所有方格。 ( 1 ≤ n , m ≤ 1 0 5 , 0 ≤ k ≤ 1 0 5 ) (1≤n,m≤10^{5} ,0≤k≤10^5) (1≤n,m≤105,0≤k≤105)
思路
err…还是看数据范围,跑 DFS 妥妥的 TLE,一开始也没大有思路优化,然后参考了 这篇blog
- 首先利用贪心的思想进行分析
如果没有障碍物那么路径肯定是螺旋矩阵形的,当前方无障碍物时肯定会优先直行,那么可以这样处理:
一直直行直至遇到障碍物再右拐,每次右拐螺旋矩阵就会缩小,到最后缩为一点时结束行走。 - 障碍物位置的获取:
数据范围 1e5,枚举的话 O ( n 2 ) O(n^2) O(n2) 会 T 掉,所以在这里首先存储障碍物的位置排序后再通过二分查找进行优化。 - 为了防止越界,还需要四个变量去维护这个螺旋矩阵的范围。
代码实现
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int n,m,k,a,b;
ll ans=1;
vector<int> r[maxn],c[maxn];
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=k;i++){
scanf("%d%d",&a,&b);
r[a].push_back(b);
c[b].push_back(a);
}
for(int i=1;i<=n;i++)
sort(r[i].begin(),r[i].end());
for(int i=1;i<=m;i++)
sort(c[i].begin(),c[i].end());
int x=1,y=1,dx,dy,pos=0;
int Xmin=0,Xmax=n+1,Ymin=0,Ymax=m+1;
bool flag=false;//起点特判
while(true){
//右移
if(pos==0){
dx=x,dy=lower_bound(r[x].begin(),r[x].end(),y)-r[x].begin();
if(dy==r[x].size()) dy=Ymax-1;
else dy=min(Ymax-1,r[x][dy]-1);
Xmin=x;
}
//下移
else if(pos==1){
dy=y,dx=lower_bound(c[y].begin(),c[y].end(),x)-c[y].begin();
if(dx==c[y].size()) dx=Xmax-1;
else dx=min(Xmax-1,c[y][dx]-1);
Ymax=y;
}
//左移
else if(pos==2){
dx=x,dy=lower_bound(r[x].begin(),r[x].end(),y)-r[x].begin()-1;
if(dy<0) dy=Ymin+1;
else dy=max(Ymin+1,r[x][dy]+1);
Xmax=x;
}
//上移
else if(pos==3){
dy=y,dx=lower_bound(c[y].begin(),c[y].end(),x)-c[y].begin()-1;
if(dx<0) dx=Xmin+1;
else dx=max(Xmin+1,c[y][dx]+1);
Ymin=y;
}
if(dx==x&&dy==y&&flag)
break;
pos=(pos+1)%4;
ans+=(ll)abs(x-dx)+(ll)abs(y-dy);
x=dx,y=dy;
flag=true;
}
if(ans==(ll)n*m-k) printf("Yes\n");
else printf("No\n");
return 0;
}