jzoj1916 [2011集训队出题] 飞飞侠 spfa

56 篇文章 0 订阅

Description


  飞飞国是一个传说中的国度,国家的居民叫做飞飞侠。飞飞国是一个N×M的矩形方阵,每个格子代表一个街区。
  然而飞飞国是没有交通工具的。飞飞侠完全靠地面的弹射装置来移动。
  每个街区都装有弹射装置。使用弹射装置是需要支付一定费用的。而且每个弹射装置都有自己的弹射能力。
  我们设第i行第j列的弹射装置有Aij的费用和Bij的弹射能力。并规定有相邻边的格子间距离是1。那么,任何飞飞侠都只需要在(i,j)支付Aij的费用就可以任意选择弹到距离不超过Bij的位置了。如下图
这里写图片描述

  (从红色街区交费以后可以跳到周围的任意蓝色街区。)
  现在的问题很简单。有三个飞飞侠,分别叫做X,Y,Z。现在它们决定聚在一起玩,于是想往其中一人的位置集合。告诉你3个飞飞侠的坐标,求往哪里集合大家需要花的费用总和最低。

Data Constraint


  20% N, M ≤ 10; Bij ≤ 20
  40% N, M ≤ 100; Bij ≤ 20
  100% 1 ≤ N, M ≤ 150; 0 ≤ Bij ≤ 10^9; 0 ≤ Aij ≤ 1000

Solution


sb题,考试的时候大概是懵了连最短路都不会了
暴力连边会t因此直接带着坐标跑,spfa大概率会被卡因此slfllf随便上
感觉题解脑洞太大了,说白了就是做成分层图跑会少很多无用的边,但是我死活改不出那种做法就算了吧

Code


#include <stdio.h>
#include <string.h>
#include <queue>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
const int INF=0x3f3f3f3f;
const int L=151;
struct pos{int x,y;};
std:: deque<pos> que;
int a[L][L],b[L][L];
int dis[3][L][L];
bool vis[L][L];
int n,m;
char prt;
int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}
void spfa(pos st,pos ed1,pos ed2,int d[L][L]) {
    while (!que.empty()) que.pop_back();
    rep(i,0,n) rep(j,0,m) d[i][j]=INF;
    fill(vis,0);
    d[st.x][st.y]=0;
    vis[st.x][st.y]=1;
    que.push_back(st);
    while (!que.empty()) {
        pos now=que.front(); que.pop_front();
        int A=a[now.x][now.y];
        int B=b[now.x][now.y];
        rep(i,max(now.x-B,1),min(now.x+B,n)) {
            int tmp=std:: abs(now.x-i);
            rep(j,max(now.y-B+tmp,1),min(now.y+B-tmp,m)) {
                if (d[now.x][now.y]+A<d[i][j]) {
                    d[i][j]=d[now.x][now.y]+A;
                    if (!vis[i][j]) {
                        vis[i][j]=1;
                        if (que.empty()) que.push_back((pos){i,j});
                        else {
                            pos fr=que.front();
                            if (dis[fr.x][fr.y]>dis[i][j]) que.push_front((pos){i,j});
                            else que.push_back((pos){i,j});
                        }
                    }
                }
            }
        }
        vis[now.x][now.y]=0;
    }
}
int main(void) {
    n=read();
    m=read();
    rep(i,1,n) rep(j,1,m) b[i][j]=read();
    rep(i,1,n) rep(j,1,m) a[i][j]=read();
    int x1=read(); int y1=read(); pos pos1=(pos){x1,y1};
    int x2=read(); int y2=read(); pos pos2=(pos){x2,y2};
    int x3=read(); int y3=read(); pos pos3=(pos){x3,y3};
    spfa(pos1,pos2,pos3,dis[0]);
    spfa(pos2,pos1,pos3,dis[1]);
    spfa(pos3,pos1,pos2,dis[2]);
    int d,ans=INF;
    if ((d=dis[1][x1][y1]+dis[2][x1][y1])<ans) {
        ans=d;
        prt='X';
    }
    if ((d=dis[0][x2][y2]+dis[2][x2][y2])<ans) {
        ans=d;
        prt='Y';
    }
    if ((d=dis[0][x3][y3]+dis[1][x3][y3])<ans) {
        ans=d;
        prt='Z';
    }
    if (ans==INF) puts("NO");
    else printf("%c\n%d\n",prt,ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值