[BZOJ1632][Usaco2007 Feb]Lilypad Pond(spfa)

题目描述

传送门

题解

刚看到这道题的时候想的很复杂,感觉每一次都有前提不是很好搞,但其实这三种都可以直接用最短路来实现。
关键是怎么实现呢?第一次写的时候是做了三遍spfa,每一次求出答案了之后再跑下一次,让下一次满足当前的条件。但是写完之后发现是不对的。显然不对啊!因为当前在跑的时候你无法保证用过的点都是上一问合法的答案。恰恰相反!我们应该在一次spfa里处理这三个问题,只不过是优先级不同而已!
这三问优先级依次递减,所以只有在优先级高的答案相同的时候才去考虑更新优先级低的。这样做一遍spfa就能保证在满足优先级高的同时尽量满足优先级低的,就非常科学了。
不过后来我又仔细想了一下,其实做三遍spfa也是可以判的。就是当f[now]+len==f[nxt]的时候,说明now在s到nxt的最短路上,这样的时候再去更新。这种判断点是否在最短路上的方法是非常常见的呀!真是傻逼了。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
#define N 50
#define LL long long

int n,m,sx,sy,tx,ty,minadd,minstep,cnt,inf;
int a[N][N],add[N][N],step[N][N];
LL sol[N][N];
bool vis[N][N];
struct hp{int x,y;};
int dx[8]={-2,-2,-1,-1,1,1,2,2};
int dy[8]={-1,1,-2,2,-2,2,-1,1};
queue <hp> q;

void spfa()
{
    memset(add,127,sizeof(add));inf=add[0][0];add[sx][sy]=0;
    memset(step,127,sizeof(step));step[sx][sy]=0;
    memset(sol,0,sizeof(sol));sol[sx][sy]=1;
    memset(vis,0,sizeof(vis));vis[sx][sy]=true;
    while (!q.empty()) q.pop();q.push((hp){sx,sy});
    while (!q.empty())
    {
        hp now=q.front();q.pop();
        vis[now.x][now.y]=false;
        for (int i=0;i<8;++i)
        {
            int x=now.x+dx[i],y=now.y+dy[i];
            if (!(x>=1&&x<=n&&y>=1&&y<=m)||a[x][y]==2) continue;
            int len;
            if (a[x][y]==0) len=1; else len=0;
            if (add[now.x][now.y]+len<add[x][y])
            {
                add[x][y]=add[now.x][now.y]+len;
                step[x][y]=step[now.x][now.y]+1;
                sol[x][y]=sol[now.x][now.y];
                if (!vis[x][y])
                {
                    vis[x][y]=true;
                    q.push((hp){x,y});
                }
            }
            else if (add[now.x][now.y]+len==add[x][y])
            {
                if (step[now.x][now.y]+1<step[x][y])
                {
                    step[x][y]=step[now.x][now.y]+1;
                    sol[x][y]=sol[now.x][now.y];
                    if (!vis[x][y])
                    {
                        vis[x][y]=true;
                        q.push((hp){x,y});
                    }
                }
                else if (step[now.x][now.y]+1==step[x][y])
                {
                    sol[x][y]+=sol[now.x][now.y];
                    if (!vis[x][y])
                    {
                        vis[x][y]=true;
                        q.push((hp){x,y});
                    }
                }
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i)
        for (int j=1;j<=m;++j)
        {
            scanf("%d",&a[i][j]);
            if (a[i][j]==3) sx=i,sy=j;
            if (a[i][j]==4) tx=i,ty=j;
        }
    spfa();
    if (add[tx][ty]==inf)
    {
        puts("-1");
        return 0;
    }
    printf("%d\n%d\n%lld\n",add[tx][ty],step[tx][ty],sol[tx][ty]);
}

总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值