最优对称路径

G - 最优对称路径
Time Limit:1000MS    Memory Limit:65535KB    64bit IO Format:%I64d & %I64u

Description

给一个n行n列的网格,每个格子里有一个1到9的数字。你需要从左上角走到右下角,其中每一步只能往上、下、左、右四个方向之一走到相邻格子,不能斜着走,也不能走出网格,但可以重复经过一个格子。为了美观,你经过的路径还必须关于“左下-右上”这条对角线对称。下图是一个6x6网格上的对称路径。
你的任务是统计所有合法路径中,数字之和最小的路径有多少条。

Input

输入最多包含25组测试数据。每组数据第一行为一个整数n(2<=n<=100)。以下n行每行包含n个1到9的数字,表示输入网格。输入结束标志为n=0。

Output

对于每组数据,输出合法路径中,数字之和最小的路径条数除以1,000,000,009的余数。

Sample Input

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

Sample Output

2 
3

Hint


思路:先把图压缩成左上角的一半  在用dij求出(0,0)点到左下-右上的斜边的各个点的最短路,然后只要斜边上的点满足最短路,就dfs深搜路径到(0.0)点,并用一个vis1标记当前坐标是否搜索过,如果搜索过直接返回该值,就是记忆化搜索,并且搜索的条件是当前的点加上当前点的权值要等于刚才走过来那个点的最短路的值才往下搜,因为要保证最短路,


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
typedef __int64 LL;
using namespace std;

LL mod = 1000000009;

const int maxn = 222222222;

int vis[210][210],head[210][210],n,cnt,d[210][210],map[210][210];

struct Edge
{
    int u[2],v[2],w;
    int next;
    Edge(int a =0,int b = 0,int e= 0,int r = 0,int c = 0)
    {
        u[0] = a; u[1] = b; w = c;v[0] = e;v[1] = r;
    }
}e[450000];

void add(Edge q)
{
    e[cnt].u[0] = q.u[0];
    e[cnt].u[1] = q.u[1];
    e[cnt].v[0] = q.v[0];
    e[cnt].v[1] = q.v[1];
    e[cnt].w = q.w;
    e[cnt].next = head[q.u[0]][q.u[1]];
    head[q.u[0]][q.u[1]] = cnt++;
}

struct node
{
    int x, y, val;
    friend bool operator <(node a, node b)
    {
        return a.val > b.val;
    }
    node (int a=0, int b=0, int c=0)
    {
        x = a; y = b;val = c;
    }
};

void dij()
{
    memset(vis,0,sizeof(vis));
    int i,j;
    for(i = 0; i<n;i++)
    {
        for(j = 0; j<n;j++)
        {
            d[i][j] = maxn;
        }
    }
    d[0][0] = map[0][0];
    priority_queue <node> q;
    q.push(node (0,0,d[0][0]));
    while (!q.empty())
    {
        node w = q.top();
        q.pop();
        if(vis[w.x][w.y] == 1)
        {
            continue;
        }
        vis[w.x][w.y] = 1;
        for(i = head[w.x][w.y];i!=-1;i=e[i].next)
        {
            int end1 = e[i].v[0];
            int end2 = e[i].v[1];
            if(d[end1][end2] > d[w.x][w.y] + e[i].w)
            {
                d[end1][end2] = d[w.x][w.y] + e[i].w;
                q.push(node (end1,end2,d[end1][end2]));
            }
        }
    }
}

int ans1,aa[4] = {1,-1,0,0},bb[4]={0,0,1,-1},vis1[210][210];
LL ans,dp[210][210];

LL dfs(int x, int y)
{
    if(x == 0&&y ==0)
        return 1;
    if(vis1[x][y] == 1)
        return dp[x][y];
    vis1[x][y] = 1;
    int i;
    dp[x][y] = 0;
    for(i = 0; i < 4; i++)
    {
        int x1 = x + aa[i];
        int y1 = y + bb[i];
        if(x1>=0&&x1<n&&y1>=0&&y1<n-x1&&(d[x1][y1]+map[x][y] == d[x][y]))
        {
            dp[x][y] =( dp[x][y]%mod + dfs(x1,y1)%mod)%mod;
        }
    }
    return dp[x][y];
}

int main()
{
    int i,j,k;
    while (scanf("%d",&n)&&n)
    {
        for(i = 0; i<n;i++)
        {
            for(j = 0; j < n; j++)
            {
                scanf("%d",&map[i][j]);
            }
        }
        for(i = 0; i<n;i++)
        {
            for(j = 0; j < n - i; j++)
            {
                if(j!=n-i-1)
                    map[i][j] += map[n-j-1][n-i-1];
            }
        }
        memset(head,-1,sizeof(head));
        cnt = 0;
        for(i = 0; i<n;i++)
        {
            for(j = 0; j < n-i; j++)
            {
                for(k = 0; k < 4; k++)
                {
                    if(i+aa[k]>=0&&i+aa[k]<n&&j+bb[k]>=0&&j+bb[k]<n-i-aa[k])
                    {
                        add(Edge(i,j,i+aa[k],j+bb[k],map[i+aa[k]][j+bb[k]]));
                    }
                }
            }
        }
        dij();
        ans1 = maxn;
        ans = 0;
        for(i = 0; i<n;i++)
        {
            if(d[i][n-i-1] < ans1)
                ans1 = d[i][n-i-1];
        }
        memset(vis1,0,sizeof(vis1));
        dp[0][0] = 1;
        for(i = 0; i < n; i++)
        {
            if(d[i][n-1-i] == ans1)
               ans=(ans%mod+dfs(i,n-1-i)%mod)%mod;
        }
        cout<<ans<<endl;

    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值