【NOIP2012模拟10.20】路径数

∥☆過路亽.° 提交于 2019-11-30 16:06:18

题目链接

路径数

题目描述

Euphemia到一个\(N*N\)的药草田里采药,她从左上角的格子田(第一行,第一列)出发,要到达右下角(第\(N\)行,第\(N\)列)的格子田,每次她可以走到与当前格子有边相邻的格子去,但她不会走已经走过的格子,而且出于对美的要求,她走过的路径是关于 左下-右上 对角线对称的。由于地势不同,在每个格子田采药都会有一个疲劳度\(T_{i,j}\),Euphemia想知道:有多少条合法路径,可以使得她采药的疲劳度最小。

输入格式:

多组数据。
每组数据第一行一个整数\(N\),接下来\(N\)行,每行$N4个非零数字(\(1,2,3...9\)中一个),表示格子田的疲劳度。
\(N=0\),输入结束。

输出格式:

对于每组数据,输出一个整数表示答案,答案%1000000009。

样例输入:

2
1 1
1 1
3
1 1 1
1 1 1
2 1 1
0

样例输出:

2
3

数据范围:

对于\(20%\)的数据满足\(N<=5\)
对于另外\(20%\)的数据满足\(N<=40\)
对于\(100%\)的数据满足\(N<=100\),不超过\(50\)组数据。

时间限制:

1S

空间限制:

256M

提示:

remove!!!

题解

根据题意可知这是一道图论题。
求最短路径的方案数。
因为她走过的路径是关于 左下-右上 对角线对称的所以我们可以把这个矩阵沿 左下-右上 对角线“对折”下来。
接下来在剩下的“三角形图”上跑dijstra求最短路,并且松弛的时候记录一下方案数就好了。
最后在对角线上统计一下最短路的方案数。
dijstra加上堆优化,时间复杂度是\((N^3)\)(节点数为\(N^2\))。
上代码:

#include<bits/stdc++.h>
#include<queue>
#define mod 1000000009
using namespace std;
int n;
int a[109][109];
int dis[109][109],s[109][109];
bool k[109][109];
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
void dij(int x,int y){
    priority_queue< pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > pp;
    dis[1][1]=a[1][1];
    s[1][1]=1;
    pp.push(make_pair(dis[x][y],x*n+y-1));
    while(!pp.empty()){
        pair<int,int> u=pp.top();
        pp.pop();
        int y=u.second%n+1,x=u.second/n;
//      printf("*%d %d*\n",x,y);
        if(k[x][y]) continue;
        k[x][y]=1;
        for(int j=0;j<4;j++){
            int xx=x+dir[j][0],yy=y+dir[j][1];
            if(xx<1 || yy<1 || xx+yy>n+1 || k[xx][yy]) continue;
//          printf("!%d %d!\n",xx,yy);
            if(dis[xx][yy]==-1 || dis[xx][yy]>dis[x][y]+a[xx][yy]){
                dis[xx][yy]=dis[x][y]+a[xx][yy];
                s[xx][yy]=s[x][y];
                pp.push(make_pair(dis[xx][yy],xx*n+yy-1));
            }
        else if(dis[xx][yy]==dis[x][y]+a[xx][yy]) s[xx][yy]+=s[x][y];
        }
    }
}
int main() {
    while(1){
        scanf("%d",&n);
        if(n==0) break;
        memset(dis,-1,sizeof(dis));
        memset(s,0,sizeof(s));
        memset(a,0,sizeof(a));
        memset(k,0,sizeof(k));
        for(int j=1;j<=n;j++)
            for(int i=1;i<=n;i++)
                scanf("%d",&a[j][i]);
        for(int j=1;j<=n;j++)
            for(int i=n-j+2;i<=n;i++)
                a[n-i+1][n-j+1]+=a[j][i];
        dij(1,1);
        int mn=-1,ss;
        for(int j=1;j<=n;j++){
//          printf("**%d %d %d**\n",j,dis[j][n-j+1],s[j][n-j+1]);
            if(mn==-1 || mn>dis[j][n-j+1]){
                mn=dis[j][n-j+1];
                ss=s[j][n-j+1];
            }
            else if(mn==dis[j][n-j+1]) ss+=s[j][n-j+1];
        }
        printf("%d\n",ss);
    }
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!