Description
Dpstr最近迷上了推冰块。冰地是一个n行m列的网格区域,第i行第j列的格子记为(i,j),也就是左上角为(1,1),右下角为(n,m)。每个格子可能是冰面、障碍物、减速带三者之一。其中,冰地外围(即第0行、第n+1行、第0列、第m+1列)的所有格子均有障碍物。除此之外,冰地内共有k个障碍物和减速带,其余格子为冰面。
初始时,有一个冰块位于(1,1)处。Dpstr每次可以选择上、下、左、右四个方向之一推动该冰块,推动后该冰块将一直沿此方向移动,直到冰块所在的格子为减速带,或冰块沿运动方向的下一个格子为障碍物时,冰块将停止运动。一旦冰块停在减速带上,该减速带即消失。
如下图,当n = 5,m = 5时,若冰块位于(3,1),且(3,4)处有一个障碍物,那么向右推动冰块(图1中A处),冰块将停在(3,3)(图1中B处);而如果(3,4)处不是障碍物而是减速带,那么冰块将停在(3,4)上(图2中B处),之后(3,4)处的减速带就会消失。
Dpstr希望通过尽量少的推动次数使得冰块停在(n,m)处。请计算Dpstr至少要推多少次冰块。
Data Constraint
对于30%的数据,2≤n≤5,2≤m≤5,0≤k≤5;
对于50%的数据,2≤n≤1,000,2≤m≤1,000,0≤k≤1,000;
对于70%的数据,2≤n≤50,000,2≤m≤50,000,0≤k≤50,000;
对于100%的数据,2≤n≤1,000,000,000,2≤m≤1,000,000,000,0≤k≤50,000,1≤xi≤n,1≤yi≤m,0≤ti≤1。
Solution
我们这道题用bfs+二分来解决。显然一个点不可能重复走两次,所以我们每次只要用二分求出离我最近的点转移过去即可。二分可以再离散化后的数组后计算。
Code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=6000005;
const int f[4][2]={{0,1},{1,0},{-1,0},{0,-1}};
struct code{
int a,b,c;
}a[maxn/100],b[maxn/100];
int v[maxn][2];
ll n,m,p,i,t,j,k,l,x,y,z,xx,yy,num,num1,s,h[maxn];
int bz[maxn];
bool cmp(code x,code y){
return x.a<y.a || x.a==y.a && x.b<y.b;
}
int pan(ll x,ll y){
ll k=(x-1)*m+y;
ll t=k%maxn;
while (h[t] && h[t]!=k) t++;
h[t]=k;return t;
}
void make(int x,int y){
int l=0,r=p,mid,t,k,xx,yy;
while (l<r){
mid=(l+r+1)/2;
if (a[mid].a>x || a[mid].a==x && a[mid].b>=y) r=mid-1;
else l=mid;
}
t=pan(x,y);
if (a[l].a!=x){
k=pan(x,1);
if (bz[k]<0) bz[k]=bz[t]+1,v[++j][0]=x,v[j][1]=1;
}else{
yy=a[l].b;
if (!a[l].c) yy++;
k=pan(x,yy);
if (bz[k]<0) bz[k]=bz[t]+1,v[++j][0]=x,v[j][1]=yy;
}
l=0,r=p;
while (l<r){
mid=(l+r)/2;
if (a[mid].a<x || a[mid].a==x && a[mid].b<=y) l=mid+1;
else r=mid;
}
if (a[l].a!=x){
k=pan(x,m);
if (bz[k]<0) bz[k]=bz[t]+1,v[++j][0]=x,v[j][1]=m;
}else{
yy=a[l].b;
if (!a[l].c) yy--;
k=pan(x,yy);
if (bz[k]<0) bz[k]=bz[t]+1,v[++j][0]=x,v[j][1]=yy;
}
}
void make1(int x,int y){
int l=0,r=p,mid,t,k,xx;
while (l<r){
mid=(l+r+1)/2;
if (b[mid].a>y || b[mid].a==y && b[mid].b>=x) r=mid-1;
else l=mid;
}
t=pan(x,y);
if (b[l].a!=y){
k=pan(1,y);
if (bz[k]<0) bz[k]=bz[t]+1,v[++j][0]=1,v[j][1]=y;
}else{
xx=b[l].b;
if (!b[l].c) xx++;
k=pan(xx,y);
if (bz[k]<0) bz[k]=bz[t]+1,v[++j][0]=xx,v[j][1]=y;
}
l=0,r=p;
while (l<r){
mid=(l+r)/2;
if (b[mid].a<y || b[mid].a==y && b[mid].b<=x) l=mid+1;
else r=mid;
}
if (b[l].a!=y){
k=pan(n,y);
if (bz[k]<0) bz[k]=bz[t]+1,v[++j][0]=n,v[j][1]=y;
}else{
xx=b[l].b;
if (!b[l].c) xx--;
k=pan(xx,y);
if (bz[k]<0) bz[k]=bz[t]+1,v[++j][0]=xx,v[j][1]=y;
}
}
void spfa(){
j=v[1][0]=v[1][1]=1;i=0;
bz[pan(1,1)]=0;
while (i<j){
x=v[++i][0];
y=v[i][1];
make(x,y);
make1(x,y);
}
printf("%d\n",bz[pan(n,m)]);
}
int main(){
///freopen("data.in","r",stdin);
freopen("ice.in","r",stdin);freopen("ice.out","w",stdout);
scanf("%d%d%d",&n,&m,&p);
for (i=1;i<=p;i++)
scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c),b[i].a=a[i].b,b[i].b=a[i].a,b[i].c=a[i].c;
b[i].a=a[i].b=m+1,b[i].b=a[i].a=n+1,b[i].c=a[i].c;
p++;
sort(a+1,a+p+1,cmp);
sort(b+1,b+p+1,cmp);
memset(bz,255,sizeof(bz));
spfa();
}