csu 1106 最优对称路径(最短路+路径数)


给一个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


题意:这题有毒,题目条件给错的,n其实是200,并不是100,然后你开100的话会给你无限T, 哎,有毒的oj

其实就是在最短路上面多加一个状态即可。。。。。然后要注意,更新来的新点如果权值一样就不要加入到队列中,不然会超时


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 9;
const int inf = 0x3f3f3f3f;
const int N = 222;
struct P
{
	int x, y, sum;
	ll cnt;
	P(int x, int y, int sum, ll cnt) :x(x), y(y), sum(sum), cnt(cnt) {}
	P() {}
	bool operator < (const P& t1) const
	{
		return sum>t1.sum;
	}
}dp[N][N];
int a[N][N], n, tmp;
int dx[] = { -1,0,1,0 };
int dy[] = { 0,1,0,-1 };
void bfs()
{
	int i, j, sum;
	priority_queue<P>que;
	que.push(P(1,1,a[1][1]+a[n][n],1));
	dp[1][1].sum = a[1][1] + a[n][n];
	dp[1][1].cnt = 1;
 	while (!que.empty()) {
		P now = que.top();
		que.pop();
		if(now.sum>dp[now.x][now.y].sum) continue;
		if(now.sum>tmp) break;
		if(now.x+now.y==n+1) {
            tmp = min(tmp,dp[now.x][now.y].sum);
            continue;
		}
		for (i = 0; i<4; i++) {
			int tx = now.x + dx[i];
			int ty = now.y + dy[i];
			if (tx<1 || tx>n || ty<1 || ty>n) continue;
			if (tx + ty == n + 1) sum = now.sum + a[tx][ty];
			else sum = now.sum + a[tx][ty] + a[n + 1 - ty][n + 1 - tx];
			if(dp[tx][ty].sum>sum) {
                dp[tx][ty].sum = sum;
                dp[tx][ty].cnt = dp[now.x][now.y].cnt;
                que.push(P(tx,ty,sum,dp[tx][ty].cnt));
			}
			else if(dp[tx][ty].sum==sum) {    // 这个点就不需要加入到队列中!!!!
                dp[tx][ty].cnt += dp[now.x][now.y].cnt;
                if(dp[tx][ty].cnt>mod) dp[tx][ty].cnt%=mod;
			}
		}
	}
}
int main()
{
	int i, j, x, y;
	while (scanf("%d", &n) && n) {
		for (i = 1; i <= n; i++)
			for (j = 1; j <= n; j++)
			{
				scanf("%d", &a[i][j]);
				dp[i][j].cnt = 0;
				dp[i][j].sum = inf;
			}
		tmp = inf;
		ll ans = 0;
		bfs();
		for (x = n; x>0; x--) {
			y = n + 1 - x;
			if (dp[x][y].sum<tmp) {
				tmp = dp[x][y].sum;
				ans = dp[x][y].cnt;
			}
			else if (dp[x][y].sum == tmp) {
				ans = (ans + dp[x][y].cnt) % mod;
			}
		}
		printf("%lld\n", ans%mod);
	}
	return 0;
}











  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值