• 洛谷P1004 方格取数


    洛谷P1004 方格取数 动态规划 多维DP

    题目描述

    设有N×N的方格图(N≤9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。如下图所示(见样例):

    A
     0  0  0  0  0  0  0  0
     0  0 13  0  0  6  0  0
     0  0  0  0  7  0  0  0
     0  0  0 14  0  0  0  0
     0 21  0  0  0  4  0  0
     0  0 15  0  0  0  0  0
     0 14  0  0  0  0  0  0
     0  0  0  0  0  0  0  0
                             B
    

    某人从图的左上角的A点出发,可以向下行走,也可以向右走,直到到达右下角的B点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
    此人从A点到B点共走两次,试找出2条这样的路径,使得取得的数之和为最大。

    输入格式

    输入的第一行为一个整数N(表示N×N的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的0表示输入结束。

    输出格式

    只需输出一个整数,表示2条路径上取得的最大的和。

    输入输出样例

    输入 #1
    8
    2 3 13
    2 6  6
    3 5  7
    4 4 14
    5 2 21
    5 6  4
    6 3 15
    7 2 14
    0 0  0
    
    输出 #1
    67

    说明/提示

    NOIP 2000 提高组第四题

    Solution

    这道题最初我还以为可以使用贪心,两遍dfs……

    但是会有一些特殊情况

    比如:

    0  1  1

    3  3  3

    1  1  0

    若用贪心,第一条路应该是“0  3  3  3  0”=9

    第二条路是“0  1  1  0  0”=2

    总和为11

    但是如果第一次走“0  1  3  3  0”=7

    第二条路走“0  3  1  1  0”=5

    总和为12

    好了,这种贪心已经被证明是错误的了

    Algorithm1

    四维DP

    由于两条路径的长度是相同的

    又为了满足DP的“递推规则”

    我在这里设前两维表示第一条路已经走到了第i行第j列

    后两维表示第二条路已经走到了第k行第l列

    那么DP[i][j][k][l]=四周DP的最大值+当前两条路走到的格子的价值

    如果i==k并且k===l

    说明这两条路走到了一起

    所以这时只要计算一遍这一格的价值。

    Code1

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cstdio>
     4 #include<cstring>
     5 #include<cmath>
     6 #include<map>
     7 #include<set>
     8 #include<queue>
     9 #include<vector>
    10 #define IL inline
    11 #define re register
    12 using namespace std;
    13 
    14 IL int read()
    15 {
    16     re int res=0;
    17     re char ch=getchar();
    18     while(ch<'0'||ch>'9')
    19         ch=getchar();
    20     while(ch>='0'&&ch<='9')
    21         res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
    22     return res;
    23 }
    24 int n;
    25 int f[10][10];
    26 int dp[10][10][10][10];
    27 int main()
    28 {
    29     n=read();
    30     int tx=read(),ty=read(),tz=read();
    31     while(tx!=0||ty!=0||tz!=0)
    32     {
    33         f[tx][ty]=tz;
    34         cin>>tx;
    35         cin>>ty;
    36         cin>>tz;
    37     }
    38     for(int i=1;i<=n;i++)
    39     for(int j=1;j<=n;j++)
    40     for(int k=1;k<=n;k++)
    41     for(int l=1;l<=n;l++)
    42     {
    43         int now=0;
    44         now=max(now,dp[i-1][j][k][l-1]+f[i][j]+f[k][l]);
    45         now=max(now,dp[i][j-1][k][l-1]+f[i][j]+f[k][l]);
    46         now=max(now,dp[i-1][j][k-1][l]+f[i][j]+f[k][l]);
    47         now=max(now,dp[i][j-1][k-1][l]+f[i][j]+f[k][l]);
    48         if(i==k&&j==l) now-=f[i][j];
    49         dp[i][j][k][l]=now;
    50     }
    51     cout<<dp[n][n][n][n];
    52     return 0;
    53 }

    Algorithm2

    事实上,我们可以发现

    由于两条路是同时走的

    所以当前的格子与起点(终点)的曼哈顿距离是相同的

    $$i+j==k+l$$

    那不就可以省去l了嘛,通过i,j,k就可以计算出相对应的合法的l!

    Code2

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cstdio>
     4 #include<cstring>
     5 #include<cmath>
     6 #include<map>
     7 #include<set>
     8 #include<queue>
     9 #include<vector>
    10 #define IL inline
    11 #define re register
    12 using namespace std;
    13 
    14 IL int read()
    15 {
    16     re int res=0;
    17     re char ch=getchar();
    18     while(ch<'0'||ch>'9')
    19         ch=getchar();
    20     while(ch>='0'&&ch<='9')
    21         res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
    22     return res;
    23 }
    24 int n;
    25 int f[10][10];
    26 int dp[10][10][10];
    27 int main()
    28 {
    29     n=read();
    30     int tx=read(),ty=read(),tz=read();
    31     while(tx!=0||ty!=0||tz!=0)
    32     {
    33         f[tx][ty]=tz;
    34         cin>>tx;
    35         cin>>ty;
    36         cin>>tz;
    37     }
    38     for(int i=1;i<=n;i++)
    39     for(int j=1;j<=n;j++)
    40     for(int k=1;k<=n&&i+j-k>0;k++)
    41     {
    42         int now=0;
    43         now=max(now,dp[i-1][j][k]+f[i][j]+f[k][i+j-k]);
    44         now=max(now,dp[i][j-1][k]+f[i][j]+f[k][i+j-k]);
    45         now=max(now,dp[i-1][j][k-1]+f[i][j]+f[k][i+j-k]);
    46         now=max(now,dp[i][j-1][k-1]+f[i][j]+f[k][i+j-k]);
    47         if(i==k) now-=f[k][i+j-k];
    48         dp[i][j][k]=now;
    49     }
    50     cout<<dp[n][n][n];
    51     return 0;
    52 }

    Attention

    记得判断两条路是否走到了同一个格子上。

    End

  • 相关阅读:
    不敢想,做个博客竟如此简单!
    我有一台服务器,能干啥?
    精品 IDEA 插件大汇总!值得收藏
    怒肝半月!Python 学习路线+资源大汇总
    怒肝 Linux 学习路线,这回不难
    多环境
    分享 10 个高星的创意项目!
    js的attribute
    选择大厂该做哪些准备?|小白进入大厂现状
    小白程序猿了解大厂的开发模式
  • 原文地址:https://www.cnblogs.com/send-off-a-friend/p/11397797.html
Copyright © 2020-2023  润新知