UVA12295 Optimal Symmetric Paths(记忆化搜索,最短路)

描述

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.

Sample Input

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

Sample Output

2
3

思路

首先说题意,有一个n*m的方格,现在要从(1,1)点走到(n,m)点,而且走的路线要沿着对角线对称,可以看着上面的图理解一下。问从在走最短路的情况下,从(1,1)走到(n,m)一共有多少种走法。

首先我们要满足题意,也就是要对称,我们可以把这个图沿着对角线对这一下,并且把对称格子的权值加起来,这样的话这个问题就变成了求(1,1)走到对角线上的格子的最短路径的走法。

那么我们从(1,1)点开始bfs利用优先队列使权值小的先出队,计算出来从起点走到每一个点的最小权值。

然后遍历一下对角线上的点,当这个点的权值等于要求的最短路的话,就从这个点进行记忆化搜索,令step[x][y]来记录从(1,1)点走到(x,y)点的最小方案数,我们可以知道step[1][1]=1,最后把方案数累加起来就好了

代码

#include <bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
typedef long long ll;
const ll N=100+20;
const ll mod=1e9+7;
ll e[N][N],n;
ll go[4][2]= {1,0,-1,0,0,1,0,-1};
ll dp[N][N];
ll step[N][N];
struct node
{
    ll x,y,w;
    node() {}
    node(ll _x,ll _y,ll _w)
    {
        x=_x;
        y=_y;
        w=_w;
    }
    bool friend operator < (node a,node b)
    {
        return a.w>b.w;
    }
};
void bfs()
{
    dp[1][1]=e[1][1];
    priority_queue<node>q;
    q.push(node(1,1,e[1][1]));
    while(!q.empty())
    {
        node now=q.top();
        q.pop();
        if(dp[now.x][now.y]<now.w)continue;
        for(ll i=0; i<4; i++)
        {
            ll xx=now.x+go[i][0];
            ll yy=now.y+go[i][1];
            if(xx>=1&&xx<=n&&yy>=1&&yy<=n-xx+1&&dp[now.x][now.y]+e[xx][yy]<dp[xx][yy])
            {
                dp[xx][yy]=dp[now.x][now.y]+e[xx][yy];
                q.push(node(xx,yy,dp[xx][yy]));
            }
        }
    }
}
ll dfs(ll x,ll y)
{
    if(step[x][y])return step[x][y];
    ll sum=0;
    for(ll i=0; i<4; i++)
    {
        ll xx=x+go[i][0];
        ll yy=y+go[i][1];
        if(xx>=1&&xx<=n&&yy>=1&&yy<=n-xx+1&&dp[x][y]==dp[xx][yy]+e[x][y])
            sum+=dfs(xx,yy)%mod;
    }
    return step[x][y]=sum;
}
int main()
{
    while(scanf("%lld",&n)&&n)
    {
        for(ll i=1; i<=n; i++)
            for(ll j=1; j<=n; j++)
                scanf("%lld",&e[i][j]);
        for(ll i=1; i<=n; i++)
            for(ll j=n-i; j>=1; j--)
            {
                ll x=n-j+1;
                ll y=n-i+1;
                e[i][j]+=e[x][y];
            }
        mem(dp,inf);
        mem(step,0);
        bfs();
        ll minn=inf;
        for(ll i=1; i<=n; i++)
            minn=min(minn,dp[i][n-i+1]);
        step[1][1]=1;
        ll ans=0;
        for(ll i=1; i<=n; i++)
            if(dp[i][n-i+1]==minn)
                ans+=dfs(i,n-i+1)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值