You have a grid of n rows and n columns. Each of the unit squares contains a non-zero digit. You
walk from the top-left square to the bottom-right square. Each step, you can move left, right, up or
down to the adjacent square (you cannot move diagonally), but you cannot visit a square more than
once. There is another interesting rule: your path must be symmetric about the line connecting the
bottom-left square and top-right square. Below is a symmetric path in a 6 × 6 grid.
Your task is to find out, among all valid paths, how many of them have the minimal sum of digits?
Input
There will be at most 25 test cases. Each test case begins with an integer n (2 ≤ n ≤ 100). Each of
the next n lines contains n non-zero digits (i.e. one of 1, 2, 3, . . . , 9). These n
2
integers are the digits
in the grid. The input is terminated by a test case with n = 0, you should not process it.
Output
For each test case, print the number of optimal symmetric paths, modulo 1,000,000,009.
题意大概是从矩形的左上角走到右下角,求权值最短的路径的个数,路径必须是关于副对角线对称。
既然要关于副对角线对称,那么其实只要我走到了副对角线上,那么这条路径就已经确定了,也就是再走到副对角线之前,走一步相当于走了两步,因为还相当于走了一步关于副对角线对称的一步,所以我们可以将对称的点权值相加,这样就变成了从起点到副对角线的最短距离,因为有5000个点,所以用spfa比较保险,然后再记录一下最短路的条路就好了,具体见代码。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
#define ll long long
#define maxn 120
#define angel 0x3f3f3f3f
const ll mod=1000000009;
int n;
int dirx[5]={0,1,-1,0,0};
int diry[5]={0,0,0,1,-1};
ll value[maxn][maxn];
ll num[maxn][maxn];//最短路径个数
int id[maxn][maxn];
ll dist[maxn][maxn];//最短路径长度
int check[maxn][maxn];
ll Min(ll x,ll y)
{
if(x>y)
return y;
return x;
}
struct co
{
int x,y;
};
void spfa()
{
queue<co>q;
check[1][1]=1;
num[1][1]=1;//起点最短路个数为1
co st;
st.x=1;
st.y=1;
q.push(st);
while(!q.empty())
{
co now=q.front();
//cout<<now.x<<" "<<now.y<<endl;
for(int i=1; i<=4; i++)
{
co next;
next.x=dirx[i]+now.x;
next.y=diry[i]+now.y;
if(next.x<1||next.y<1||next.x>n||next.y>n+1-next.x)
continue;
if(dist[next.x][next.y]>dist[now.x][now.y]+value[next.x][next.y])
{
dist[next.x][next.y]=dist[now.x][now.y]+value[next.x][next.y];
num[next.x][next.y]=num[now.x][now.y];//更新最短路径个数
if(!check[next.x][next.y])
{
q.push(next);
check[next.x][next.y]=1;
}
}
else if(dist[next.x][next.y]==dist[now.x][now.y]+value[next.x][next.y])
{
num[next.x][next.y]+=num[now.x][now.y];//更新最短路径个数 画个图就很好理解
if(!check[next.x][next.y])
{
q.push(next);
check[next.x][next.y]=1;
}
}
}
q.pop();
check[now.x][now.y]=0;
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
if(n==0)
break;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n+i-1; j++)
{
dist[i][j]=angel;
check[i][j]=0;
num[i][j]=0;
}
}
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
scanf("%d",&value[i][j]);
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n+1-i; j++)
{
if(j==n+1-i)
continue;
value[i][j]+=(value[n+1-j][n+1-i]);//对称权值相加
}
}
dist[1][1]=value[1][1];
spfa();
ll minn=angel;
for(int i=1; i<=n; i++)
minn=Min(minn,dist[i][n+1-i]);
ll ans=0;
for(int i=1; i<=n; i++)
{
if(dist[i][n+1-i]==minn)
ans=(ans+num[i][n+1-i])%mod;
}
printf("%lld\n",ans);
}
return 0;
}