NOIP2000 提高组T4: 方格取数

本文介绍了一个寻找两条路径,使得从起点到终点收集数值之和最大的算法问题。通过暴力搜索和动态规划两种方法解决该问题,并给出了详细的代码实现。

题目描述

题目链接

设有 N × N N \times N N×N 的方格图 ( N ≤ 9 ) (N \le 9) (N9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字 0 0 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 A A 点出发,可以向下行走,也可以向右走,直到到达右下角的 B B B 点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字 0 0 0)。
此人从 A A A 点到 B B B 点共走两次,试找出 2 2 2 条这样的路径,使得取得的数之和为最大。

输入格式

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

输出格式

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

样例 #1

样例输入 #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

算法思想(暴力出奇迹+80分)

根据题目要求:

此人从 A A A 点到 B B B 点共走两次,试找出 2 2 2 条这样的路径,使得取得的数之和为最大。

可以使用DFS同时搜索这两条路径,分别记录走到每一个位置 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) ( x 2 , y 2 ) (x_2,y_2) (x2,y2)时取得的数字之和 s 1 s_1 s1 s 2 s_2 s2。当走到终点时,打擂台求出最大值。

注意,当两条路径走到同一位置时,只能取一次数

代码实现

#include <iostream>
using namespace std;
const int N = 15;
int w[N][N];
int n, ans;
void dfs(int x1, int y1, int s1, int x2, int y2, int s2)
{
	//越界
    if(x1 > n || y1 > n || x2 > n || y2 > n) return;
    //到达终点
    if(x1 == n && y1 == n && x2 == n && y2 == n)
    {
        ans = max(ans, s1 + s2 + w[n][n]); //加上终点上的数
        return;
    }
    //走到相同位置,只取一次数
    if(x1 == x2 && y1 == y2) 
    {
        dfs(x1 + 1, y1, s1 + w[x1][y1], x2 + 1, y2, s2); //路径1向下走,路径2向下走
        dfs(x1 + 1, y1, s1 + w[x1][y1], x2, y2 + 1, s2); //路径1向下走,路径2向右走
        dfs(x1, y1 + 1, s1 + w[x1][y1], x2 + 1, y2, s2); //路径1向右走,路径2向下走
        dfs(x1, y1 + 1, s1 + w[x1][y1], x2, y2 + 1, s2); //路径1向右走,路径2向右走
    }
    else
    {
        dfs(x1 + 1, y1, s1 + w[x1][y1], x2 + 1, y2, s2 + w[x2][y2]);
        dfs(x1 + 1, y1, s1 + w[x1][y1], x2, y2 + 1, s2 + w[x2][y2]);
        dfs(x1, y1 + 1, s1 + w[x1][y1], x2 + 1, y2, s2 + w[x2][y2]);
        dfs(x1, y1 + 1, s1 + w[x1][y1], x2, y2 + 1, s2 + w[x2][y2]);
    }
}
int main()
{
    int x, y, z;
    cin >> n;
    while(cin >> x >> y >> z, x || y || z) w[x][y] = z;
    dfs(1, 1, 0, 1, 1, 0);
    cout << ans << endl;
    return 0;
}

算法思想(动态规划+100分)

状态表示

通过上面的暴力搜索发现其实要求的就是两条路径到达 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) ( x 2 , y 2 ) (x_2,y_2) (x2,y2)时得的数字之和的最大值,因此可以用状态 f ( x 1 , y 1 , x 2 , y 2 ) f(x_1,y_1,x_2,y_2) f(x1,y1,x2,y2)表示。

状态计算

计算状态 f ( x 1 , y 1 , x 2 , y 2 ) f(x_1,y_1,x_2,y_2) f(x1,y1,x2,y2)的值可以分为两类:

  • 走到相同位置,即 x 1 = x 2 、 y 1 = y 2 x_1=x_2、y_1=y_2 x1=x2y1=y2,那么在该位置上只取一次数。此时应该取下面 4 4 4种情况的最大值,再加上 w ( x 1 , y 1 ) w(x_1,y_1) w(x1,y1)
    • 路径1从上面 ( x 1 − 1 , y 1 ) (x_1-1,y_1) (x11,y1)走到 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)、路径2从上面 ( x 2 − 1 , y 2 ) (x_2-1,y_2) (x21,y2)走到、 ( x 2 , y 2 ) (x_2,y_2) (x2,y2),即 f ( x 1 − 1 , y 1 , x 2 − 1 , y 2 ) f(x_1-1,y_1,x_2-1,y_2) f(x11,y1,x21,y2)
    • 路径1从上面 ( x 1 − 1 , y 1 ) (x_1-1,y_1) (x11,y1)走到 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)、路径2从左面 ( x 2 , y 2 − 1 ) (x_2,y_2-1) (x2,y21)走到、 ( x 2 , y 2 ) (x_2,y_2) (x2,y2),即 f ( x 1 − 1 , y 1 , x 2 , y 2 − 1 ) f(x_1-1,y_1,x_2,y_2-1) f(x11,y1,x2,y21)
    • 路径1从上面 ( x 1 , y 1 − 1 ) (x_1,y_1-1) (x1,y11)走到 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)、路径2从上面 ( x 2 − 1 , y 2 ) (x_2-1,y_2) (x21,y2)走到、 ( x 2 , y 2 ) (x_2,y_2) (x2,y2),即 f ( x 1 , y 1 − 1 , x 2 − 1 , y 2 ) f(x_1,y_1-1,x_2-1,y_2) f(x1,y11,x21,y2)
    • 路径1从上面 ( x 1 , y 1 − 1 ) (x_1,y_1-1) (x1,y11)走到 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)、路径2从左面 ( x 2 , y 2 − 1 ) (x_2,y_2-1) (x2,y21)走到、 ( x 2 , y 2 ) (x_2,y_2) (x2,y2),即 f ( x 1 , y 1 − 1 , x 2 , y 2 − 1 ) f(x_1,y_1-1,x_2,y_2-1) f(x1,y11,x2,y21)
  • 走到不同位置,取两个位置上的数。应该取上面 4 4 4种情况的最大值,再加上 w ( x 1 , y 1 ) w(x_1,y_1) w(x1,y1) w ( x 2 , y 2 ) w(x_2,y_2) w(x2,y2)

代码实现

#include <iostream>
using namespace std;
const int N = 15;
//f[x1][y1][x2][y2]表示两条路径到达(x1,y1)、(x2,y2)时得的数字之和的最大值
int w[N][N], f[N][N][N][N];
int main()
{
    int n, x, y, z;
    cin >> n;
    while(cin >> x >> y >> z, x || y || z) w[x][y] = z;
    for(int x1 = 1; x1 <= n; x1 ++)
        for(int y1 = 1; y1 <= n; y1 ++)
            for(int x2 = 1; x2 <= n; x2 ++)
                for(int y2 = 1; y2 <= n; y2 ++)
                {
                	//求4种情况的最大值
                    int t = max(max(f[x1 - 1][y1][x2 - 1][y2], f[x1 - 1][y1][x2][y2 - 1]),
                            max(f[x1][y1 - 1][x2 - 1][y2], f[x1][y1 - 1][x2][y2 - 1]));                    
                    if(x1 == x2 && y1 == y2) //走到相同位置,只取一次数
                        f[x1][y1][x2][y2] = t + w[x1][y1];
                    else //走到不同位置,取两个位置上的数
                        f[x1][y1][x2][y2] = t + w[x1][y1] + w[x2][y2]; 
                }
    cout << f[n][n][n][n] << endl;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少儿编程乔老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值