bzoj 1967 //1967: [Ahoi2005]CROSS 穿越磁场

bzoj 1967 //1967: [Ahoi2005]CROSS 穿越磁场   //在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1967

更多题解,详见https://blog.csdn.net/mrcrack/article/details/90228694BZOJ刷题记录

摘自https://wenku.baidu.com/view/140615c10c22590102029d0c.html

//1967: [Ahoi2005]CROSS 穿越磁场
//在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1967
//先从样例入手,在纸上写写画画,再读题干
//题意很快弄懂.
//思路摘自https://blog.csdn.net/clover_hxy/article/details/52984896?utm_source=blogxgwz1
/*
题解:离散化+spfa
正方形的个数很小,但是坐标范围比较大,所以考虑将整个坐标系离散化,但是离散的时候,那些原本不直接相连,离散化后相连的边中间要加入空的一列。
离散化之后处理连边,将跨越边界的边的权值赋值为1,其他的为0,然后用spfa跑最短路即可。
*/
//代码图形化,是重中之中,纯粹的代码是孤芳自赏,没有生命力,没有延续性.

样例原始呈现形式:

样例离散化后,呈现形式:

样例离散化后,进行标记

样例离散化后,根据标记,建图过程 (画图花了6个小时,希望对后来者有用。2019-8-29 10:50)


//题目还是有些问题的,明明(其中,1 < SX, SY, TX, TY < 10000),样例输入部分出现了0 0 3 4
//该程序涉及数据较多,建议边编写,边打印数据,这样能尽早发现问题.
//虽然编写一段,测试一段,但数据量过大,还是容易晕,可想而知,要是全写好再测试,会有多糟糕.2019-8-29 20:34
//样例一直无法通过,查了半天,发现是一直引以为豪的邻接表编码错误.2019-8-29 21:55
//cnt++,e[cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],head[u]=cnt;//此处错写成cnt++,e[cnt].to=v,e[cnt].w=w,head[u]=cnt;
//修改,样例通过,提交AC,看了看时间,编码花了4个小时.2019-8-29 21:59
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 510
int n,x[maxn],y[maxn],cnt_x,cnt_y,mp_x[maxn],mp_y[maxn],num_x[maxn],num_y[maxn],vis[maxn][maxn],head[maxn*maxn],cnt,can[maxn*maxn],d[maxn*maxn],q[maxn*maxn*10],h,t,max_x,max_y;
struct node{
    int to,next,w;//to指向连接点,next下一条边
}e[maxn*maxn*2*2];//为什么是maxn*maxn*2*2,可画图找规律
int cmp_x(int a,int b){
    return x[a]<x[b];
}
int cmp_y(int a,int b){
    return y[a]<y[b];
}
void add_edge(int u,int v,int w){//临接表 无向图
    cnt++,e[cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],head[u]=cnt;//此处错写成cnt++,e[cnt].to=v,e[cnt].w=w,head[u]=cnt;
    cnt++,e[cnt].to=u,e[cnt].w=w,e[cnt].next=head[v],head[v]=cnt;//此处错写成cnt++,e[cnt].to=u,e[cnt].w=w,head[v]=cnt;
}
int spfa(int sx,int sy,int tx,int ty){
    int u,v,b;
    memset(d,127,sizeof(d)),memset(can,0,sizeof(can));
    v=(sx-1)*max_y+sy,d[v]=0,can[v]=1,h=t=1,q[t]=v,t++;
    while(h<t){
        u=q[h],b=head[u];
        while(b){
            v=e[b].to;
            if(d[v]>d[u]+e[b].w){
                d[v]=d[u]+e[b].w;
                if(!can[v]) can[v]=1,q[t]=v,t++;
            }
            b=e[b].next;
        }
        can[u]=0;//漏了此句
        h++;
    }
    return d[(tx-1)*max_y+ty];
}
int main(){
    int i,j,X,Y,C,sx,sy,tx,ty,m,X1,Y1;//max_x 离散化后,最大的x坐标值;max_y 离散化后,最大的y坐标值;
    scanf("%d",&n);
    cnt_x=0,cnt_y=0;
    for(i=1;i<=n;i++){//效果详见 图 样例原始呈现形式:
        scanf("%d%d%d",&X,&Y,&C);
        x[++cnt_x]=X,num_x[cnt_x]=cnt_x,x[++cnt_x]=X+C,num_x[cnt_x]=cnt_x;
        y[++cnt_y]=Y,num_y[cnt_y]=cnt_y,y[++cnt_y]=Y+C,num_y[cnt_y]=cnt_y;//num_y[i]=j 自小到大第i位次的元素脚标是j
    }
    scanf("%d%d%d%d",&sx,&sy,&tx,&ty);
    x[++cnt_x]=sx,num_x[cnt_x]=cnt_x,x[++cnt_x]=tx,num_x[cnt_x]=cnt_x;
    y[++cnt_y]=sy,num_y[cnt_y]=cnt_y,y[++cnt_y]=ty,num_y[cnt_y]=cnt_y;
    x[++cnt_x]=-1,num_x[cnt_x]=cnt_x,x[++cnt_x]=20000,num_x[cnt_x]=cnt_x;//界定机器人活动边界,
    y[++cnt_y]=-1,num_y[cnt_y]=cnt_y,y[++cnt_y]=20000,num_y[cnt_y]=cnt_y;//-1最小值,20000最大值
    
    sort(num_x+1,num_x+1+cnt_x,cmp_x),m=0,num_x[0]=0,x[0]=0;
    for(i=1;i<=cnt_x;i++)//x离散化,效果详见 图 样例离散化后,呈现形式:
        if(x[num_x[i]]!=x[num_x[i-1]]){//前后两点x坐标不同
            if(x[num_x[i]]==x[num_x[i-1]]+1)m++,mp_x[num_x[i]]=m;
            else m+=2,mp_x[num_x[i]]=m;//原本不直接相连,离散化后相连边中间要加入空列
        }else mp_x[num_x[i]]=m;//x[num_x[i]]==x[num_x[i-1]];
    max_x=m;
    
    sort(num_y+1,num_y+1+cnt_y,cmp_y),m=0,num_y[0]=0,y[0]=0;
    for(i=1;i<=cnt_y;i++)y离散化,效果详见 图 样例离散化后,呈现形式:
        if(y[num_y[i]]!=y[num_y[i-1]]){//前后两点y坐标不同
            if(y[num_y[i]]==y[num_y[i-1]]+1)m++,mp_y[num_y[i]]=m;
            else m+=2,mp_y[num_y[i]]=m;//原本不直接相连,离散化后相连边中间要加入空列
        }else mp_y[num_y[i]]=m;//y[num_y[i]]==y[num_y[i-1]];
    max_y=m;

    memset(vis,0,sizeof(vis)),cnt_x=0,cnt_y=0;//对图中节点打标记,效果详见 图 样例离散化后,进行标记
    for(i=1;i<=n;i++){//枚举磁场,对磁场边界的点打标记
        X=mp_x[++cnt_x],X1=mp_x[++cnt_x],Y=mp_y[++cnt_y],Y1=mp_y[++cnt_y];
        for(j=X;j<X1;j++)vis[j][Y]+=1,vis[j][Y1]+=1;//为了区分,平行于x轴上的点标记1
        for(j=Y;j<Y1;j++)vis[X][j]+=2,vis[X1][j]+=2;//为了区分,平行于y轴上的点标记2
    }
    sx=mp_x[++cnt_x],tx=mp_x[++cnt_x];
    sy=mp_y[++cnt_y],ty=mp_y[++cnt_y];
    
    memset(head,0,sizeof(head)),cnt=0;
    for(i=1;i<=max_x;i++)//根据标记建图,效果相见 图 样例离散化后,根据标记,建图过程
        for(j=1;j<=max_y;j++)
            if(vis[i][j]>=3)continue;
            else if(vis[i][j]==1)add_edge((i-1)*max_y+j,(i-1)*max_y+j+1,1);//向上连线
            else if(vis[i][j]==2)add_edge((i-1)*max_y+j,i*max_y+j,1);//向右连线
            else add_edge((i-1)*max_y+j,(i-1)*max_y+j+1,0),add_edge((i-1)*max_y+j,i*max_y+j,0);//向上连线 向右连线,步调一致//vis[i][j]==0
    
    printf("%d\n",spfa(sx,sy,tx,ty));//spfa算法
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值