洛谷 P1004 方格取数 - Java题解

文章描述了一道编程竞赛题目,要求在N×N的网格中找到两条从左上角到右下角的路径,使得路径上数字之和最大。传统的动态规划方法无法解决这个问题,因为局部最优解不一定能得出全局最优解。因此,文章提出了使用四维动态规划数组同时考虑两个路径的方法,遍历并更新所有可能的路径组合,最终得出最大和。代码示例展示了如何用Java实现这个算法。
摘要由CSDN通过智能技术生成

题目描述

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

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

输入格式

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

输出格式

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

输入输出样例

输入

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

输出

67

说明/提示

NOIP 2000 提高组第四题

题目分析

如果只看从A点到B点这一条路径的话,那么这可以看出这是一道经典的dp(动态规划)问题,由于点只能向下或向右走,所以每个点的值只需要考虑它上一点或左一点,即递推式为

f(i, j) = max{f(i-1, j) ,  f(i, j-1)}

然而该题是找出两条路径,使它们的加和最大。如果只考虑单纯的两次动态规划进行加和,那该题是行不通的,因为第一次走的时候如果有两个不同选择使得数之和相同,那么dp或者dfs都是任选一条的,但此时选择不同的路可能会影响到第二次走的路线,进而导致结果不同,即局部最优并不能推导出全局最优。

所以我们就想到了能不能两点同时进行动态规划,那么就只能开出四维数组了,每两维分别来表示两个点的位置坐标。由于B点到A点与A点到B点,是一个道理,所以我们可以将B到A的路线反过来dp,那么已经知道了该思路我们就可以得到一个复合的递推式:

f(ai, aj, bi, bj) = max{

                                max{ f(ai-1, aj, bi-1, bj), f(ai-1, aj, bi, bj-1) } , 

                                max{ f(ai, aj-1, bi-1, bj), f(ai, aj-1, bi, bj-1) }

                                }

注意:遍历完一次不为0的点后,要将该店的值变为0

代码如下:


import java.util.Scanner;

public class P1004 {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[][] arr = new int[n + 3][n + 3];
        int x = sc.nextInt();
        int y = sc.nextInt();
        int num = sc.nextInt();
        while (x != 0 && y != 0 && num != 0){
            arr[x][y] = num;
            x = sc.nextInt();
            y = sc.nextInt();
            num = sc.nextInt();
        }

        int[][][][] dp = new int[n + 3][n + 3][n + 3][n + 3];
        for (int ai = 1; ai <= n; ai++) {
            for (int aj = 1; aj <= n; aj++) {
                for (int bi = 1; bi <= n; bi++) {
                    for (int bj = 1; bj <= n; bj++) {
                        dp[ai][aj][bi][bj] = Math.max(
                            Math.max(dp[ai - 1][aj][bi - 1][bj], dp[ai - 1][aj][bi][bj - 1]), 
                            Math.max(dp[ai][aj - 1][bi - 1][bj], dp[ai][aj - 1][bi][bj - 1])
                        );
                        if(ai == bi && aj == bj){// a和b坐标相同,该点只加一次(0+0=0)
                            dp[ai][aj][bi][bj] += arr[ai][aj];
                        }else {
                            dp[ai][aj][bi][bj] += arr[ai][aj] + arr[bi][bj];
                        }
                    }
                }
            }
        }
        System.out.println(dp[n][n][n][n]);
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值