题目链接:点击查看
题目大意:Euphemia到一个N*N的药草田里采药,她从左上角的格子田(第一行,第一列)出发,要到达右下角(第N行,第N列)的格子田,每次她可以走到与当前格子有边相邻的格子去,但她不会走已经走过的格子,而且出于对美的要求,她走过的路径是关于 左下-右上 对角线对称的。由于地势不同,在每个格子田采药都会有一个疲劳度Tij,Euphemia想知道:有多少条合法路径,可以使得她采药的疲劳度最小。
题目分析:因为要关于副对角线对称,所以不妨直接将矩阵沿着副对角线对折,因为要求最短路,所以不妨直接求出以点(1,1)为起点的单源最短路,期间用dp维护一下最短路路径的数量,最后累加一下就好了,这个dp我是直接从网上拿的模板,迪杰斯特拉+dp
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=110;
const int mod=1e9+9;
const int b[4][2]={0,1,0,-1,1,0,-1,0};
int maze[N][N],n;
bool vis[N][N];
int d[N][N];
LL dp[N][N];
struct Node
{
int x,y,w;
Node(int X,int Y,int W)
{
x=X;
y=Y;
w=W;
}
bool operator<(const Node& a)const
{
return w>a.w;
}
};
void Dijkstra()
{
priority_queue<Node>q;
memset(vis,false,sizeof(vis));
memset(d,inf,sizeof(d));
memset(dp,0,sizeof(dp));
dp[1][1]=1;
d[1][1]=maze[1][1];
q.push(Node(1,1,d[1][1]));
while(q.size())
{
Node cur=q.top();
int x=cur.x,y=cur.y;
q.pop();
if(vis[x][y])
continue;
vis[x][y]=true;
for(int i=0;i<4;i++)
{
int xx=x+b[i][0];
int yy=y+b[i][1];
if(xx<=0||yy<=0||xx>n||yy>n||xx+yy>n+1)
continue;
int w=maze[xx][yy];
if(d[xx][yy]>d[x][y]+w)
{
d[xx][yy]=d[x][y]+w;
dp[xx][yy]=dp[x][y];
q.push(Node(xx,yy,d[xx][yy]));
}
else if(d[xx][yy]==d[x][y]+w)
dp[xx][yy]=(dp[xx][yy]+dp[x][y])%mod;
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
while(scanf("%d",&n)!=EOF&&n)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&maze[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<n-i+1;j++)
maze[i][j]+=maze[n-j+1][n-i+1];
Dijkstra();
int mmin=inf;//找出最短路
for(int i=1;i<=n;i++)
mmin=min(mmin,d[i][n-i+1]);
LL ans=0;//维护答案
for(int i=1;i<=n;i++)
if(d[i][n-i+1]==mmin)
ans=(ans+dp[i][n-i+1])%mod;
printf("%lld\n",ans);
}
return 0;
}