题意:
给一个n*n的方格,每个方格有一个数,代表它身边的格子到它的路径长度,求从(1,1)到(n,n)关于 的最短路径方案数对 取模
输入:
有多组测试样例,第一行输入一个正整数n,代表 正方形的大小,接下来n行每行n个数
输出:
从(1,1)到(n,n)关于 的最短路径方案数对 取模的答案
样例:
Sample Input
2
1 1
1 1
3
1 1 1
1 1 1
2 1 1
0
Sample Output
2
3
思路:
由于最短路径要关于 对称,呢么我们可以把这个正方形沿 对折,将权值加到对称的格子上,然后求(1,1)到对角线上的最短路径,我们可以通过图中(u,v)关系递推出(1,1)到对角线上的方案数,由于
dis[v]=dis[u]+w
呢么,dp[v]的数量就是dp[v]+dp[u]
,最后根据得到的dp[ ],枚举(1,1)到对角线最短距离的方案数,求和取模 即可
注意:要按格子中数的含义进行建图,如果按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();
}
}