SCAU 10687 东方迷宮

SCAU 10687 东方迷宮

这里给出启发式的题解和比较简单的做法。(代码五十行)

题意:在一个n*n的网格中,每个网格上有一个值(1~9),网格之间可能有板子,即相邻两格子可能不互通,从左上角走到右下角,找出一条最短路,其路上总权值最大。

1.思路:要找出一条最短路,使得路上权值之和最大。
你看到这题你首先想到了什么做法呢?
如果是:枚举出所有最短路再算权值。
没错,这就是优秀大学生最喜欢的暴力法,恭喜你,可以枪毙了。

你可以在纸上画一画,你会发现,如果你想走出最短路,只能向右或者向下,也就是不能回头。想到这一步,就可以很容易
做出这道题了(才怪)。考虑一下,当前格子,只能向下或者向右,变一下思路,也就是当前格子只能由上或者左的格子走来。

动态规划四个大字已经呼之欲出了(bfs也可以但我觉得dp比较帅)

当前格子最大值=max(上面的格子最大值,左边的格子最大值)+当前格子的初始值a[i][j];
即dp[i][j]=max(dp[i-1][j],dp[i][j-1])+a[i][j];

方程出来了,仅仅是开始,我们还要寻找转移条件。
根据题意,两个格子之间互通的条件是两个格子之间没有板子。
嗯,那么在dp的时候加上这样的判定就好了。
嗯?你觉得结束了?
不不不(我当时就以为这样结束了,WA得很惨)。
两个格子之间互通,不代表走最短路的时候可以互通。
比如:
2|3 4 5 6
6 7 2 3 1
这样一个图,(0,0)右边有一个板子,因为最短路的关系,所以(0,0)右边一行都不能走,推dp的时候也不能由这些点得来。
也就是说我们还要加一个标记,v[i][j],表示这个点是否能够探索到,1为可以,0为不行,初始设v[1][1]=1。

到这里思路部分真正完结了。但是你可能还有疑问?如何处理板子?

2.处理

某黄姓老师曾说过,优秀大学生最喜欢暴力。

暴力的想一下,如何表示两点之间有板子?
两个点,四个坐标,当然是开一个四维数组dx[][][][]。
如果你是这么想的,恭喜你,(这次不枪毙了),虽然说n最大不到100,但是四个一百乘起来达到一亿,如果你有无限内存的话可以考虑这么玩。

仔细想一下,相邻的两个点,他们的x或y坐标必定有一个相等。
那么我们可以减少一维,开两个数组,dy[i][j-1][j]表示第i行的j-1与j两个格子有板子,dx[j][i-1][i]同理。

其实优化到这一步已经可以做题了,但我们不满于此,仔细思考一下,相邻两点除了一维坐标相等,另一维其实只相差1,处于一个线性的关系
我们完全可以再去掉一维坐标,用dy[i][j]表示第i行的j-1与j之间的互通情况,dx[j][i]同理。

实话说,相对于其他几个题解,我对自己的处理方法很满意。
压缩到这里,我已经满足了,有爽到。

下面给出代码。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int g[111][111],v[111][111],dx[111][111],dy[111][111],dp[111][111];

int main()
{
    
    
    int n,i,j,k,m,a,b,c,d,T,t=1;
    char aaa;
    cin >> T;
    while(T--){
    
    
        memset(dx,0,sizeof(dx));
        memset(dy,0,sizeof(dy));
        memset(dp,0,sizeof(dp));
        memset(v,0,sizeof(v));v[1][1]=1;
        cin >> n ;
        scanf("%c",&aaa);
        for(i=1;i<=n;++i){
    
    
            for(j=1;j<=n;++j){
    
    
                scanf("%c",&aaa);
                g[i][j]=aaa-'0';
                dp[i][j]=g[i][j];
            }
            scanf("%c",&aaa);
        }
        cin >> m ;
        for(i=1;i<=m;++i){
    
    
            scanf("%d%d%d%d",&a,&b,&c,&d);
            if(a>c)swap(a,c);
            if(b>d)swap(b,d);
            if(a==c)
                dy[a+1][d+1]=1;
            else if(b==d)
                dx[b+1][c+1]=1;
        }
        for(i=1;i<=n;++i){
    
    
            for(j=1;j<=n;++j){
    
    
                if(i-1>0&&dx[j][i]==0&&v[i-1][j]){
    
    
                    dp[i][j]=max(dp[i-1][j]+g[i][j],dp[i][j]);
                    v[i][j]=1;
                }
                if(j-1>0&&dy[i][j]==0&&v[i][j-1]){
    
    
                    dp[i][j]=max(dp[i][j-1]+g[i][j],dp[i][j]);
                    v[i][j]=1;
                }
            }
        }
        printf("Case %d: %d\n",t,dp[n][n]);
        ++t;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_45699242/article/details/105257737