湖南省第七届程序设计竞赛 最优对称路径

来源:互联网 发布:Java工程打包 编辑:程序博客网 时间:2024/05/29 07:40

http://acm.nyist.net/JudgeOnline/problem.php?pid=564
湖南省第七届大学生计算机程序设计竞赛
题目G
最优对称路径
给一个n行n列的网格,每个格子里有一个1到9的数字。你需要从左上角走到右下角,其中每一步只能往上、下、左、右四个方向之一走到相邻格子,不能斜着走,也不能走出网格,但可以重复经过一个格子。为了美观,你经过的路径还必须关于“左下-右上”这条对角线对称。下图是一个6x6网格上的对称路径。

你的任务是统计所有合法路径中,数字之和最小的路径有多少条。
输入
输入最多包含25组测试数据。每组数据第一行为一个整数n(2<=n<=100)。以下n行每行包含n个1到9的数字,表示输入网格。输入结束标志为n=0。
输出
对于每组数据,输出合法路径中,数字之和最小的路径条数除以1,000,000,009的余数。
样例输入
2
1 1
1 1
3
1 1 1
1 1 1
2 1 1
0

样例输出
2
3

开始看着题感觉就是先搜索出最短路径然后记忆化搜索,后来听别人一说最短路径不就用最短路径求么(QwQ,好像是欸。

最短路径没啥好说的没有负环,先将图沿对角线对折然后直接spfa,主要是记忆化搜索,按照一般的模式我开了一个三维数组
dp[x][y][dis] 表示从 (1, 1) 点开始,到达 (x, y) 点已经走了 dis 的路程,然后从(x, y)到达对角线的的距离是最短距离的路径数。
开始的时候我大致的算了一些内存,保守估计是200 * 200 * 3600 * 4 / 1024 /1024 = 549.31640625MB内存,不过我还是硬着头皮写完了,交了一发果然是意料之中的MLE,因为学校的局域网OJ上有这个题目,我正好能用admin,为了满足我那无耻的虚荣心,所以我就无耻的把内存改的很大。。。然后又无耻的交了一发,然后就意想不到的TLE,坑爹啊有木有,没办法了,反正都已经改了也不差再改一次,然后又无耻的把时间改成了10s,最后终于无耻的过了。。。。
交对了之后我至少确定自己的想法还是没有错的,至于刚刚的超时应该是因为dp数组太大,dp的效率太低了,然后我就开始想优化。。。。
最后我想了好久突然发现到达每个格子都有一个最短路径,如果搜索到某个点后累加的距离不等于到这个点的最短距离就直接返回0。 如果到达对角线距离要最短,那么在到达对角线的过程中的每一条路也都要是最短路径,这样就能保证在搜索到对角线的时候可能是最短路径。
因为到达每个点的最短路径的值只有一个,所有我的数组就不需要开到三维了。只需要 dp[x][y]
表示到达(x, y) 点后是最短路径,然后从(x, y)到达对角线也是最短路径的最短路径数量。

#include <queue>#include <cstdio>#include <cstring>#include <iostream>using namespace std;struct node{    int x, y, val;};const int inf = 0x7fffffff;const int mod = 1000000009;const int Move[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};  //移动到四个方向queue<int>que;int n, res;int dis[205][205];int mapn[205][205];bool vis[205][205];int dp[205][205];int spfa(int sx, int sy)  //spfa{    memset(dis, 63, sizeof(dis)); //相当于初始化为inf    memset(vis, false, sizeof(vis));    while(!que.empty())que.pop(); //清空队列    dis[sx][sy] = 0;  //表示(1,1)到(sx, sy)的最短距离    vis[sx][sy] = true;  //标记入队    que.push(sx*1000+sy); //压缩点然后入队    while(!que.empty()){        int rw = que.front();  //出队        que.pop();        sx = rw / 1000;        sy = rw % 1000;        vis[sx][sy] = false;        for(int i = 0; i < 4; i++){            int nx = Move[i][0] + sx;            int ny = Move[i][1] + sy;            if(nx < 1 || nx > n || ny < 1 || ny > n || nx + ny > n+1)  //超出范围或越过对角线                continue;            if(dis[nx][ny] > dis[sx][sy] + mapn[sx][sy] + mapn[n-sy+1][n-sx+1]){ //松弛                dis[nx][ny] = dis[sx][sy] + mapn[sx][sy] + mapn[n-sy+1][n-sx+1];                if(!vis[nx][ny]){                    vis[nx][ny] = true;                    que.push(nx*1000+ny); //入队                }            }        }    }    int minn = inf;     for(int i = 1; i <= n; i++){        if(dis[i][n-i+1] + mapn[i][n-i+1] < minn){            minn = dis[i][n-i+1] + mapn[i][n-i+1]; // 求出最短距离        }    }    return minn;}int Dfs(int x, int y, int sum) //记忆化搜索{    if(sum != dis[x][y])  //累加的距离如果不是最短距离        return 0;    if(x + y == n+1){ // 到达对角线        return dp[x][y] = (mapn[x][y]+sum == res);  //是否是最短距离    }    if(dp[x][y] != -1)  //搜索过了就直接返回        return dp[x][y];    dp[x][y] = 0;  //开始搜索    for(int i = 0; i < 4; i++){        int nx = Move[i][0] + x;        int ny = Move[i][1] + y;        if(nx < 1 || nx > n || ny < 1 || ny > n || nx + ny > n+1)//超出范围或越过对角线            continue;        dp[x][y] = (dp[x][y] + Dfs(nx, ny, sum + mapn[x][y] + mapn[n-y+1][n-x+1])) % mod;    }    return dp[x][y];}int main(){    while(~scanf("%d", &n) && n){        memset(dp, -1, sizeof(dp));        for(int i = 1; i <= n; i++){            for(int k = 1; k <= n; k++){                scanf("%d", &mapn[i][k]);            }        }        res = spfa(1, 1);        memset(vis, false, sizeof(vis));        printf("%d\n", Dfs(1, 1, 0));    }    return 0;}
0 0
原创粉丝点击