2020NYIST个人积分赛第一场 G (spfa()+对折枚举最短路径)

题意:

给一个n*n的方格,每个方格有一个数,代表它身边的格子到它的路径长度,求从(1,1)到(n,n)关于 y = x y=x 的最短路径方案数对 1 e 9 + 9 1e9+9 取模

输入:

有多组测试样例,第一行输入一个正整数n,代表 n n n*n 正方形的大小,接下来n行每行n个数

输出:

从(1,1)到(n,n)关于 y = x y=x 的最短路径方案数对 1 e 9 + 9 1e9+9 取模的答案

样例:

Sample Input
2
1 1
1 1
3
1 1 1
1 1 1
2 1 1
0
Sample Output
2
3

思路:

由于最短路径要关于 y = x y=x 对称,呢么我们可以把这个正方形沿 y = x y=x 对折,将权值加到对称的格子上,然后求(1,1)到对角线上的最短路径,我们可以通过图中(u,v)关系递推出(1,1)到对角线上的方案数,由于 dis[v]=dis[u]+w呢么,dp[v]的数量就是dp[v]+dp[u],最后根据得到的dp[ ],枚举(1,1)到对角线最短距离的方案数,求和取模 1 e 9 + 9 1e9+9 即可

注意:要按格子中数的含义进行建图,如果按add(i,j),在自身到自身不为0时,spfa()会陷入死循环

参考代码:

#include<bits/stdc++.h>
using namespace std;
#define inf 1<<25
typedef long long ll;
const int maxn=1e6+5;
const int mod=1e9+9;
struct node
{
    int to,nex,w;
} edge[maxn<<1];
int head[maxn],cnt,dis[maxn];
bool vis[maxn];
int dp[maxn],n;
int mp[105][105];
int dir[4][2]= {0,-1,0,1,-1,0,1,0};
void add(int u,int v,int w)
{
    edge[cnt]=node{v,head[u],w};
    head[u]=cnt++;
}
void spfa(int u)
{
    queue<int>q;
    memset(dis,0x3f,sizeof dis);
    memset(vis,false,sizeof vis);
    memset(dp,0,sizeof dp);
    q.push(u);
    dis[u]=0;
    dp[u]=1;
    while(!q.empty())
    {
        u=q.front();
        vis[u]=false;
        q.pop();
        for(int i=head[u]; ~i; i=edge[i].nex)
        {
            int v=edge[i].to;
            int w=edge[i].w;
            if(dis[u]+w<dis[v])
            {
                dis[v]=dis[u]+w;
                dp[v]=dp[u];
                if(!vis[v])
                {
                    q.push(v);
                    vis[v]=true;
                }
            }
            else if(dis[v]==dis[u]+w)
                dp[v]=(dp[u]+dp[v])%mod;
        }
    }
}
void build_mp()
{
    for(int i=0; i<n; i++)
        for(int j=0; j<n; j++)
            cin>>mp[i][j];
    //按对称轴对折
    for(int i=0; i<n-1; i++)
        for(int j=0; j<n-i-1; j++)
            mp[i][j]+=mp[n-j-1][n-i-1];
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<n-i; j++)
        {
            for(int k=0; k<4; k++)
            {
                int xx=i+dir[k][0];
                int yy=j+dir[k][1];
                if(xx<0||xx>=n||yy<0||yy>=n||xx+yy>n-1)continue;
                add(n*i+j,xx*n+yy,mp[xx][yy]);
            }
        }
    }
}
void solv()
{
    spfa(0);
    int i=0,j=n-1;
    int minx=inf;
    while(~j)
    {
        minx=min(minx,dis[i*n+j]);
        i++,j--;
    }
    int ans=0;
    i=0,j=n-1;
    while(~j)
    {
        if(dis[n*i+j]==minx)
            ans=(ans+dp[i*n+j])%mod;
        i++,j--;
    }
    cout<<ans<<endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin>>n&&n)
    {
        memset(head,-1,sizeof head);
        build_mp();
        solv();
    }

}
发布了254 篇原创文章 · 获赞 25 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/yangzijiangac/article/details/105276740