洛谷 1606

    这道题与最短路有关应该是比较容易看出的,然后如果没有第二问,我们怎么建图都可以,比如说将莲花与莲花之间建一条边权为0的边,莲花与水建一条边权为1的边,水与水之间建一条边权为1的边,然后跑最短路。这样虽然能够解决第一问,但第二问却无从下手,我们可以感觉到第二问的方案数应该与最短路的方案数有关,但是如果按照上述建图的话,我们很难发现什么关系,因为如果经过的水面相同而经过的莲花不同,会被算作是不同的最短路,而实际上方案却是一样的,我们考虑不同的最短路指的是这条路径上经过的点不完全相同,那如果我们能在跑最短路时只经过水面,这样的最短路方案数一定是题目中要求的方案数,那我们怎么办呢,我们考虑其实莲花与莲花之间的边是没有意义的,它不过是为了保证连通性,同理,除起点和终点外的莲花与水之间的边也是没有意义的,那么我们考虑换一种建图方式,对于每个水,我们将从它能直接跳到或经过若干莲花后能跳到的水连边,将起点和终点与水连边,这样跑最短路,我们发现经过这样建图后最短路上的点除起点和终点是莲花之外,其余的点全是水,这样不同的最短路就对应着经过了不同的水,也就对应着不同的放莲花的方案。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 130005
int n,m,g[35][35],sx,tx,sy,ty,l,cnt;
int pre[maxn],last[maxn],other[maxn];
int quex[1000000],quey[1000000],que[1000000];
const int dx[9]={0,1,1,2,2,-1,-1,-2,-2};
const int dy[9]={0,-2,2,-1,1,-2,2,-1,1};
int flag[35][35],dis[1005];
bool vis[1005][1005],vist[maxn];
long long f[1005];

void connect(int x,int y)
{
    l++;
    pre[l]=last[x];
    last[x]=l;
    other[l]=y;    
}

void bfs(int fx,int fy)
{
    flag[fx][fy]=++cnt;
    int h=1,t=0;
    for (int i=1;i<=8;i++) 
    {
        int xx=fx+dx[i];
        int yy=fy+dy[i];
        if (xx<1||xx>n||yy<1||yy>m) continue;
        if (flag[xx][yy]==cnt) continue;
        flag[xx][yy]=cnt;
        if (g[xx][yy]!=2&&g[xx][yy]!=0) 
        {
            t++;
            quex[t]=xx;quey[t]=yy;
            int id1=(fx-1)*m+fy;
            int id2=(xx-1)*m+yy;
            if (vis[id1][id2]) continue;
            connect(id1,id2);
            connect(id2,id1);
            vis[id1][id2]=vis[id2][id1]=1;    
        }
        else if (g[xx][yy]==0)     
        {
            int id1=(fx-1)*m+fy;
            int id2=(xx-1)*m+yy;
            if (vis[id1][id2]) continue;
            connect(id1,id2);
            connect(id2,id1);
            vis[id1][id2]=vis[id2][id1]=1;    
        }
    }
    while (h<=t) 
    {
        int x=quex[h],y=quey[h];h++;
        for (int i=1;i<=8;i++) 
        {
            int xx=x+dx[i];
            int yy=y+dy[i];
            if (xx<1||xx>n||yy<1||yy>m) continue;
            if (flag[xx][yy]==cnt) continue;
            flag[xx][yy]=cnt;
            if (g[xx][yy]==0) 
            {
                int id1=(fx-1)*m+fy;
                int id2=(xx-1)*m+yy;
                if (vis[id1][id2]) continue;
                connect(id1,id2);
                connect(id2,id1);
                vis[id1][id2]=vis[id2][id1]=1;
            }
            else if (g[xx][yy]!=2&&g[xx][yy]!=0) 
            {
                t++;
                quex[t]=xx;quey[t]=yy;    
            }
        }
    }
}

void spfa(void)
{
    memset(dis,53,sizeof dis);
    dis[(sx-1)*m+sy]=0;
    que[1]=(sx-1)*m+sy;
    int h=1,t=1;
    while (h<=t) 
    {
        int u=que[h];h++;
        vist[u]=0;
        for (int p=last[u];p;p=pre[p]) 
        {
            int v=other[p];
            if (dis[v]>dis[u]+1) 
            {
                dis[v]=dis[u]+1;    
                if (!vist[v]) 
                {
                    que[++t]=v;
                    vist[v]=1;    
                }
            }
        }
    }
}

void solve(void)
{
    memset(vist,0,sizeof vist);
    que[1]=(sx-1)*m+sy;f[que[1]]=1;
    int h=1,t=1;
    while (h<=t) 
    {
        int u=que[h];h++;
        for (int p=last[u];p;p=pre[p]) 
        {
            int v=other[p];
            if (dis[v]==dis[u]+1) 
            {
                f[v]+=f[u];
                if (!vist[v]) 
                {
                    que[++t]=v;
                    vist[v]=1;    
                }
            }
        }
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) 
        for (int j=1;j<=m;j++) 
        {
            scanf("%d",&g[i][j]);
            if (g[i][j]==3) {sx=i;sy=j;}
            if (g[i][j]==4) {tx=i;ty=j;}
        }
    bfs(sx,sy);
    bfs(tx,ty);
    for (int i=1;i<=n;i++) 
        for (int j=1;j<=m;j++) 
            if (g[i][j]==0) bfs(i,j);
    spfa();
    if (dis[(tx-1)*m+ty]>1e7) 
    {
        printf("-1\n");
        return 0;
    }
    printf("%d\n",dis[(tx-1)*m+ty]-1);
    solve();
    printf("%lld\n",f[(tx-1)*m+ty]);
    return 0;    
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值