Codevs 2059 逃出克隆岛 BFS || SPFA

逃出克隆岛


做法一:SPFA 。
建图是关键。终于把此坑补上, 貌似判能否踏过传送阵而不传送并没有什么卵用,判的话就麻烦了。

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#define MAX_V (5000 + 50)
#define MAX_E (MAX_V << 4)
using namespace std;

const int dx[] = {0,1,0,-1,0};
const int dy[] = {0,0,1,0,-1};
int V = 0, E = 0, tot = 0, C;

char maps[MAX_V][MAX_V];
int first[MAX_V], nxt[MAX_E << 1], dis[MAX_V], door[MAX_V];
int vis[MAX_V][MAX_V];
bool used[MAX_V];

struct edge{
    int from, to, cost;
}es[MAX_E << 1];

void build(int ff, int tt, int dd)
{
//  cout << ff << " " << tt << " " << dd << endl;
    es[++tot] = (edge){ff,tt,dd};
    nxt[tot] = first[ff];
    first[ff] = tot;
}

queue <int> q;
void spfa(int s)
{
    memset(dis,63,sizeof(dis));
    dis[s] = 0;
    q.push(s);
    used[s] = 1;
    while(q.size())
    {
        int x = q.front();
        q.pop();
        used[x] = 0;
        for(int i = first[x]; i != -1; i = nxt[i])
        {
            int v = es[i].to;
            if(dis[v] > dis[x] + es[i].cost)
            {
                dis[v] = dis[x] + es[i].cost;
                if(!used[v])
                {
                    q.push(v);
                    used[v] = 1;
                }
            }
        }
    }
}

int main()
{
    int n, m, cnt = 0;
    cin >> n >> m >> C;
    for(int i = 1; i <= n; i ++)
        scanf("%s", maps[i]+1);

    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
            vis[i][j] = ++V;

    int Ts, Te;
    memset(first,-1,sizeof(first));
    for(int i = 1; i <= n; i ++)
    {
        for(int j = 1; j <= m; j ++)
        {
            if(maps[i][j] == 'P')
                door[++cnt] = vis[i][j];
            if(maps[i][j] == '#')
                continue;
            if(maps[i][j] == 'Y')
                Ts = vis[i][j];
            if(maps[i][j] == 'C')
                Te = vis[i][j];
            for(int k = 1; k <= 4; k ++)
            {
                int nx = i + dx[k];
                int ny = j + dy[k];
                if(nx >= 1 && nx <= n && ny >= 1 && ny <= m)
                {
                    if(maps[nx][ny] == '#')
                        continue;
                    if(maps[nx][ny] == '*')
                        build(vis[i][j], vis[nx][ny], C);
                    else build(vis[i][j], vis[nx][ny], 0);
                }
            }
        }
    }

    for(int i = 2; i <= cnt; i ++)
    {
        build(door[1], door[i], 0);
        build(door[i], door[1], 0);
    }
    spfa(Ts);

    if(dis[Te] >= 0x3f3f3f3f)
        puts("screw you!");
    else printf("%d\n", dis[Te]);
    return 0;
}

做法二:bfs。
据说比spfa快。不是很难调。思路:读图时存起所有的传送阵的坐标,用bfs求最短路,每个坐标结构体有3个参数,分别是x,y,dis,dis表示当前点距离起点的最短距离,从起点开始扩展节点,若果遇到‘*’那么下一层时dis+=cost(代码里cost == t),bool 一个变量 表示是否是第一次遇到传送阵(如果有多个传送阵,那么我只需要第一个进入,如果第二次进入传送阵,一定不会比第一次进入优),然后使用读入时存起来的所有传送阵的信息,把除了第一次遇到的传送阵的传送阵们都加入队列作为待扩展节点,顺便将这些传送阵变成‘#’,不会再走这里,理由在前面括号里。
以上就是整个bfs 思路,但这个会超时,于是加一个dc教的强劲剪枝:如果当前的点的dis已经比我们之前搜过的ans大了,那么这条路肯定不是更优解,return;这个剪枝十分强♂劲;

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

const int MAXN = 5000 + 50;
int n, m, t;
int sx, sy, ex, ey, tot = 0;
char maps[MAXN][MAXN];
bool vis[MAXN][MAXN];
int ans = 2017483000;
int dx[] = {0,1,0,-1,0};
int dy[] = {0,0,1,0,-1};

bool had_in = 0;
struct Point{
    int x, y, dis;
}P[MAXN << 1];

queue <Point> q;
void bfs(int x, int y)
{
    int cnt = 0;
    q.push((Point){x,y,0});
    vis[x][y] = 1;
    while(q.size())
    {
        Point p = q.front();
        q.pop();
        if(p.dis > ans)
            return;
        if(p.x == ex && p.y == ey)
            ans = min(ans,p.dis);
        for(int i = 1; i <= 4; i ++)
        {
            int nx = p.x + dx[i];
            int ny = p.y + dy[i];
            int nd = p.dis;
            if(nx >= 1 && nx <= n && ny >= 1 && ny <= m && maps[nx][ny] != '#')
            {
                if(maps[nx][ny] == '*')
                {
                    maps[nx][ny] = '#';
                    q.push((Point){nx,ny,p.dis+t});
                }
                else if(maps[nx][ny] == 'P' && !had_in)
                {
                    had_in = 1;
                    for(int i = 1; i <= tot; i ++)
                    {
                        if(P[i].x != nx || P[i].y != ny)
                        {
                            maps[P[i].x][P[i].y] = '#';
                            q.push((Point){P[i].x,P[i].y,p.dis});
                        }
                    }
                }
                else q.push((Point){nx,ny,p.dis});
            }
        }
    }
}

int main()
{
    cin >> n >> m >> t;
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
        {
            cin >> maps[i][j];
            if(maps[i][j] == 'Y')
                sx = i, sy = j;
            else if(maps[i][j] == 'C')
                ex = i, ey = j;
            else if(maps[i][j] == 'P')
                P[++tot] = ((Point){i,j,0});
        }
    bfs(sx,sy);
    if(ans == 2017483000)
        cout << "screw you!" << endl; 
    else cout << ans << endl;
    return 0; 
}
以上:2016-07-29 22:00

2016-9-28 20:15:01 重打:
2~3 ms codevs

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;

const int MAXN = 5000 + 50;
const int MAXP = 500 + 5;
const int dx[] = {0,0,-1,0,1};
const int dy[] = {0,1,0,-1,0};
int n, m, cost, sx, sy, ex, ey, tot = 0, ans = 1e9;
char maps[MAXN][MAXN];
bool vis[MAXN][MAXN];

struct Point{
    int x, y, c;
}P[MAXP];

queue <Point> q;
void bfs(int sx, int sy)
{
    vis[sx][sy] = 1;
    q.push((Point){sx,sy,0});
    while(q.size())
    {
        Point now = q.front();
        q.pop();
        for(int i = 1; i <= 4; i ++)
        {
            int nx = now.x + dx[i];
            int ny = now.y + dy[i];
            int nc = now.c;
            if(nx >= 1 && nx <= n && ny >= 1 && ny <= m && maps[nx][ny] != '#' && !vis[nx][ny])
            {
                vis[nx][ny] = 1;
                if(maps[nx][ny] == 'C')
                    ans = min(ans, nc);
                else if(maps[nx][ny] == 'P')
                {
                    maps[nx][ny] = '#';
                    for(int i = 1; i <= tot; i ++)
                    {
                        if(P[i].x == nx && P[i].y == ny)
                            continue;
                        maps[nx][ny] = '#';
                        q.push((Point){P[i].x,P[i].y,nc});
                    }
                }
                else if(maps[nx][ny] == '*')
                    if(nc+cost <= ans)
                        q.push((Point){nx,ny,nc+cost});
            }
        }
    }
}

int main()
{
    cin >> n >> m >> cost;
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
        {
            char c = getchar();
            while(c != 'Y' && c != 'C' && c != '#' && c != '*' && c != 'P')
                c = getchar();
            maps[i][j] = c;
            if(c == 'Y')
                sx = i, sy = j;
            else if(c == 'C')
                ex = i, ey = j;
            else if(c == 'P')
                P[++tot] = (Point){i,j,0};
        }
    bfs(sx,sy);
    if(ans != 1e9)
        cout << ans << endl;
    else puts("screw you!");
    return 0;
}
以上 2016-07-29 22:00

改了一下 小伙伴 的程序, 结果0 ms 过了, Orz。
我认为主要是他的程序中, 第一次找到 终点C 的时候就不在继续往下搜索了, 我自己找了一会也没找出反例, 这应该的确是最优的。Orz

以下是程序(我改):

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<queue>
using namespace std;
const int maxn=1005;
int dx[]={0,1,0,-1,0};
int dy[]={0,0,1,0,-1};
int n,m,v,k=0,cnt=0;
char map[maxn][maxn];
bool used[maxn][maxn];
int sx,sy,ex,ey;
struct dqs1
{
    int x,y,cost;
};
queue <dqs1>q;
struct dqs2
{
    int x,y;
}sb[maxn];
void move(dqs1 now)
{   
    dqs1 tui;
    for(int i=1;i<=k;i++)
    {
        if(sb[i].x != now.x || sb[i].y != now.y)
        {
            tui.x=sb[i].x;
            tui.y=sb[i].y;
            tui.cost = now.cost;
            q.push(tui);
            map[tui.x][tui.y]='#';  
        }

    }
}
bool check(int x,int y)
{
    if(x>=1&&x<=n&&y>=1&&y<=m&&used[x][y]==0&&map[x][y]!='#')
    return true;
    return false;
}
void bfs()
{
    dqs1 begin;
    begin.x=sx;
    begin.y=sy;
    begin.cost=0;
    used[sx][sy]=1;
    q.push(begin);
    while(!q.empty())
    {
        dqs1 head=q.front();
        q.pop();
        for(int i=1;i<=4;i++)
        {
            dqs1 now;
            now.x=head.x+dx[i];
            now.y=head.y+dy[i];
            now.cost = head.cost;
            if(check(now.x, now.y))
            {
                used[now.x][now.y]=1;
                if(map[now.x][now.y]=='*')
                {
                    now.cost=head.cost+v;
                    q.push(now);
                }
                else if(map[now.x][now.y]=='P')
                    move(now);//
                else if(map[now.x][now.y]=='C')
                {
                    printf("%d\n", now.cost);
                    return;
                }

            }
        }
    }
    printf("screw you!\n");
    return;
}
int main()
{
    scanf("%d%d%d",&n,&m,&v);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        cin>>map[i][j];
        if(map[i][j]=='Y')
        {
            sx=i;
            sy=j;
        }
        if(map[i][j]=='P')
        {
            k ++;
            sb[k].x=i;
            sb[k].y=j;
        }
    }
    bfs();
    return 0;
} 
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值