【10687 东方迷宮】 SCAU 新生赛题解

41 篇文章 0 订阅
32 篇文章 3 订阅

在这里插入图片描述在这里插入图片描述

题意:从左上顶点开始,到右下顶点结束,求最短路径上的最大权值。

动态规划思路(递归法):

其实题目简化了问题,看出来了就好做很多。
题目说要路径最短,那肯定不能往回走每一步要么向下要么向右,这样其实就很好写状态转移方程了,如果用递推法的话,状态转移方程为

dp[i][j] = max(dp[i-1][j]+dp[i][j-1])+a[i][j]

即当前位置由上边或者左边得到,取前一个状态的最优解即可。
最后返回dp[n][n]。
同时注意当前元素和要选的元素之间有没有障碍

我这里用递归写,
用三维数组dp[i][j][pos]表示当前位置(i,j)由上个位置经pos(往右,往下)操作过来的状态。这样写的好处就是在两个位置之间如果有障碍的话,可以提前标记两个位置不能互相通过。
同时用方向数组表示向下和向右。详见代码。

#include <iostream>
#include <vector>
#include <cstdio>
#include <map>
#include <string>
#include <cmath>
#include <cstring>
#include <algorithm>
#define maxn 105
using namespace std;
static int inf =0x3f3f3f3f;
typedef  long long ll;
ll dp[maxn][maxn][4] ;  //maxn*maxn  grid  and  2 directions
ll no[maxn][maxn][4];  //  some blocks
ll grid[maxn][maxn];
ll  direction[2][2] =  {{1,0},{0,1}};  // 2derections :  down and right        方向数组
int a[2][2];
ll n;
int num;
ll max(ll a, ll b)
{
    return a>b?a:b;
}
ll  DP(ll i, ll j, ll pos)
{
   // cout<<i<<' '<<j<<endl;
    if(dp[i][j][pos]!=-1)  return dp[i][j][pos];
        if(i<1||i>n||j<1||j>n)  return dp[i][j][pos]=-inf;
   if(i==n&&j==n)
    {
         return dp[i][j][pos] = grid[i][j];
    }
    // main code follows
    ll  maxone = -1;
    for(int k=0; k<2; k++)
    {
        if(no[i+direction[k][0]][ j+direction[k][1] ][k])  continue;
        maxone = max(DP(i+direction[k][0],j+direction[k][1], k)+grid[i][j],maxone);
    }
    return dp[i][j][pos] = maxone ;

}
void blocks()
{
     for(int i=1; i<=num; i++)
        {
            cin>>a[0][0]>>a[0][1]>>a[1][0]>>a[1][1];
            a[0][0]++;
            a[0][1]++;
            a[1][1]++;
            a[1][0]++;
            if(a[0][0]==a[1][0])
            {
                if(a[0][1]>a[1][1])
                {
                    int temp = a[0][1];
                    a[0][1] =  a[1][1];
                    a[1][1] = temp;
                }
               // no[a[0][0]][a[0][1]][] = 1;
                no[a[1][0]][a[1][1]][1] = 1;
            }
            else if(a[0][1]==a[1][1])
            {
                if(a[0][0]>a[1][0])
                {
                    int temp = a[0][0];
                    a[0][0] = a[1][0];
                    a[1][0] = temp;
                }
               // no[a[0][0]][a[0][1]][0] = 1;
                no[a[1][0]][a[1][1]][0] = 1;
            }
        }
}
int main()
{
    int kase;
    cin>>kase;
    int m = 1;
    while(kase--)
    {
        cin>>n;
        char c = getchar();
        memset(dp,-1,sizeof(dp));
        memset(no,0,sizeof(no));
        for(int i=1; i<=n; i++)
        {
            string s;
            getline(cin,s);
            for(int j=1; j<=n; j++)
             {
                    grid[i][j] = s[j-1] - '0';
             }
        }
        cin>>num;
        blocks();
       printf("Case %d: %lld\n",m++,DP(1,1,1));
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值