P1004 [NOIP2000 提高组] 方格取数


原题链接

P1004
题目类型: 普 及 + / 提 高 {\color{lightgreen} 普及+/提高} +/
AC记录:Accepted

题目大意

设有 N × N N\times N N×N的方格图,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字 0 0 0
某人从图的左上角的 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条路径上取得的最大的和。

S a m p l e \mathbf{Sample} Sample I n p u t \mathbf{Input} Input

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

S a m p l e \mathbf{Sample} Sample O u t p u t \mathbf{Output} Output

67

H i n t & E x p l a i n \mathbf{Hint\&Explain} Hint&Explain

A
  0> >0 > 0   0   0   0   0   0
      v   v                        
  0   0   13 >0   0   6   0   0
      v       v                   
  0   0   0   0   7   0   0   0
      v       v                   
  0   0   0   14  0   0   0   0
      v       v                   
  0   21  0   0 > 0 > 4 > 0 > 0
      v                       v   
  0   0 > 15  0   0   0   0   0
          v                   v   
  0   14  0   0   0   0   0   0
          v                   v   
  0   0   0 > 0 > 0 > 0 > 0 > 0
                                 B
注:两个方向标就是两条路线共同走的地方。
权值和为(21+15)+(13+14+4)=67

数据范围

对于 100 % 100\% 100%的数据, N ≤ 9 N \le 9 N9

解题思路

妥妥的 d p dp dp题目,属于多进程的最优化决策问题
很多人第一时间会想到用贪心来找出两条最优路线,然后相加其值
但是这个方法很容易举出反例,如:

0 0 3 0 2 0
0 0 3 0 0 0
0 0 3 0 0 0
0 0 0 0 4 0
0 0 0 0 4 0
0 0 3 0 0 0

如果按照上面的策略,第一条路线为:

0 > 0 > 3   0   2   0
        v
0   0   3   0   0   0
        v
0   0   3   0   0   0
        v
0   0   0 > 0 > 4   0
                v
0   0   0   0   4   0
                v
0   0   3   0   0 > 0

此路线权值为17,随后地图上剩下了一个3和一个2,而他们两个不能同时取到,所以不是最优解。最优解为:

0 > 0 > 3   0   2   0
        v
0   0   3   0   0   0
        v
0   0   3   0   0   0
        v
0   0   0   0   4   0
        v
0   0   0   0   4   0
        v
0   0   3 > 0 > 0 > 0

随后剩下的2,4,4都能一并取到,两条路线总权值为22


既然确定了是用 d p dp dp,那么问题来了,怎么设状态呢?
我们可以把两条不同的路线看成两个人同时从 A A A点走到 B B B,并把路中的权值加起来再取其中的最大值。最后 B B B点所代表的值,就是答案。而状态转移方程也出来了:
f i , j , k , l f_{i,j,k,l} fi,j,k,l为当第一个人从 A A A点走到 ( i , j ) (i,j) (i,j)时,第二个人从 B B B点走到 ( k , l ) (k,l) (k,l)时的权值和, a i , j a_{i,j} ai,j为在位置 ( i , j ) (i,j) (i,j)上的数,则:
设 p = m a x ( f i − 1 , j , k − 1 , l , f i − 1 , j , k , l − 1 , f i , j − 1 , k − 1 , l , f i , j − 1 , k , l − 1 ) . f i , j , k , l = { 0 i = 0 或 j = 0 或 k = 0 或 l = 0 p + a i , j i = k 且 j = l p + a i , j + a k , l i ≠ k 或 j ≠ l 设p=max(f_{i-1,j,k-1,l},f_{i-1,j,k,l-1},f_{i,j-1,k-1,l},f_{i,j-1,k,l-1}). \\ f_{i,j,k,l}=\begin{cases} 0 & i=0或j=0或k=0或l=0 \\ p+a_{i,j} & i=k且j=l \\ p+a_{i,j}+a_{k,l} & i\ne k或j\ne l \end{cases} p=max(fi1,j,k1,l,fi1,j,k,l1,fi,j1,k1,l,fi,j1,k,l1).fi,j,k,l=0p+ai,jp+ai,j+ak,li=0j=0k=0l=0i=kj=li=kj=l
这里说的 p p p其实就是第一个人状态可以转移过来的两个方向第二个人状态可以转移过来的两个方向再取其中的最大值。
第一个条件不用说了。
第二个条件就是当两个人同时走到了同一个位置,则那个位置上的数只能加一次。
第三个条件就是两个人没有走到一起,则两个人位置上的数都可以加到权值和里面。
复杂度 Θ ( n 4 ) \Theta(n^4) Θ(n4)


最后,祝大家早日
请添加图片描述

上代码

#include<iostream>

using namespace std;

const int      dir[5][5]={{0,0,0,0,0},{0,-1,0,-1,0},{0,-1,0,0,-1},{0,0,-1,-1,0},{0,0,-1,0,-1}};
int            f[18][18][18][18];
int            a[18][18];
int            n;

int main()
{
    cin>>n;
    int x,y,z;
    while(cin>>x>>y>>z&&x&&y&&z)
        a[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++)
                {
                    f[x1][y1][x2][y2]=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]+=a[x1][y1];
                    else
                        f[x1][y1][x2][y2]+=a[x1][y1]+a[x2][y2];
                }
    // 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++)
    //                 cout<<"f["<<x1<<"]["<<y1<<"]["<<x2<<"]["<<y2<<"] = "<<f[x1][y1][x2][y2]<<endl;
    cout<<f[n][n][n][n]<<endl;
    return 0;
}

完美切题 ∼ \sim

拓展练习

在洛谷上找题目的时候还看到了这道题的加强版:
P2045 方格取数加强版
难度: 省 选 / N O I − \color{purple}省选/NOI- /NOI
有兴趣的可以去看一下,就是把原题中的走 2 2 2次改成了走 k ( 1 ≤ k ≤ 10 ) k(1\le k\le 10) k(1k10)次。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值