矩阵迷宫 HihoCoder - 1702 DP解法

#1702 : 矩阵迷宫

时间限制: 10000ms
单点时限: 1000ms
内存限制: 256MB

描述

给定一个NxN的方格矩阵迷宫,每个格子中都有一个整数Aij。最初小Hi位于迷宫左上角的格子A11,他每一步可以向右或向下移动,目标是移动到迷宫的出口——右下角ANN。  

小Hi需要支付的代价包括路径中经过的所有格子中的整数之和,以及改变移动方向需要支付的代价。  

小Hi第一次改变方向的代价是1,第二次的代价是2,第三次的代价是4,…… 第K次的代价是2K-1。  

请你帮小Hi算出要离开迷宫代价最小的路径,并输出要支付的代价。

输入

第一行一个整数N。  (1 ≤ N ≤ 100)  

以下N行每行N个整数,代表矩阵A。  (1 ≤ Aij ≤ 100)

输出

从左上角到右下角路径的最小的代价。

样例输入
3  
1 3 5  
1 1 2  
5 1 1
样例输出
9

题意:求从左上角到右下角(只能向右或者向下走)经过的路上的最小代价和(包括改变方向的代价,和经过的坐标的代价),

第一次改变方向的代价是1,第二次的代价是2,第三次的代价是4,…… 第K次的代价是2K-1。 
思路:因为只能向右或者向下走,所以要往动态规划上想,坐标,方向,和拐弯的次数。

dp【i】【j】【k】【h】表示到坐标(i,j)这个位置,并且当前的方向为k(0表示下,1表示右),已经转弯h次的最小代价。

(这个代价先不包括h次转弯,最后再算)。

一共分为四种情况,面朝下向下走,面朝右向下走,面朝下向右走,面朝右向右走;

计算到dp[n][n][2][20]为止, 因为n*n的图,不能往回走,所以一定经过2*n-1个坐标,这些坐标代价最大2*100*100,所以拐弯代价不会很大,及2*20就远远大于坐标代价的极限值了。数组最后一维算到20足以。

代码:

#include <iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<queue>
#include<algorithm>
#define inf 0x3f3f3f3f
#define N 200500
#define LL long long
#define mem(a,b) memset(a,b,sizeof(a));
using namespace std;
int a[105][105];
int dp[105][105][2][23];
int minn(int x,int y)
{
    return x>y?y:x;
}
int main()
{
    mem(dp,inf);//初始化为极限大
    int n;
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            scanf("%d",&a[i][j]);
    dp[1][1][1][0]=dp[1][1][0][0]=a[1][1];//初始坐标
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            for(int h=0; h<20; h++)//开始状态转移
            {
                if(i<n)//如果不为最后一行,那么就可以往下走
                {
                    if(dp[i][j][0][h]!=inf)//当前位置代价不为极限大的话
                        dp[i+1][j][0][h]=minn(dp[i+1][j][0][h],dp[i][j][0][h]+a[i+1][j]);//面朝下向下走一格,不需要转向
                    if(dp[i][j][1][h]!=inf)
                        dp[i+1][j][0][h+1]=minn(dp[i+1][j][0][h+1],dp[i][j][1][h]+a[i+1][j]);//面朝右向下走一格,要转向
                }
                if(j<n)//如果可以向右走的话
                {
                    if(dp[i][j][0][h]!=inf)
                        dp[i][j+1][1][h+1]=minn(dp[i][j+1][1][h+1],dp[i][j][0][h]+a[i][j+1]);//面朝下向右走,要转向
                    if(dp[i][j][1][h]!=inf)
                        dp[i][j+1][1][h]=minn(dp[i][j+1][1][h],dp[i][j][1][h]+a[i][j+1]);//面朝右向右走,不用转向
                }
            }
    int ans=inf;
    for(int i=0; i<=20; i++)
    {
        ans=minn(ans,dp[n][n][0][i]+(1<<i)-1);//遍历终点的所有情况,存最小值
        ans=minn(ans,dp[n][n][1][i]+(1<<i)-1);//最后计算拐弯i次的代价
    }
    printf("%d\n",ans);
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值