JZOJ4861【NOIP2016提高A组集训第7场11.4】推冰块

12 篇文章 0 订阅
1 篇文章 0 订阅

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();
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值