P1004 [NOIP2000 提高组] 方格取数

P1004 [NOIP2000 提高组] 方格取数

按照惯例,先来一遍题目~
设有 N×N 的方格图 (N≤9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字 0。
某人从图的左上角的 A点出发,可以向下行走,也可以向右走,直到到达右下角的 B 点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字 0)。
此人从 A 点到 B 点共走两次,试找出 2 条这样的路径,使得取得的数之和为最大。

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

输出格式
只需输出一个整数,表示 2 条路径上取得的最大的和。
思路奉上
很明显,这是一道动态规划的题目~
(在洛谷上做第一道动态规划题目的时候,我还很是头疼,想不清楚那个方程,今天可是顺利多了,看来还是要多多练习的哈)
不啰嗦了,先讲思路
题目的意思呢,就是要找两条路,取了路上的数字,使两条路得到的和最大,一遇到这样的问题,动态规划必须安排~
两条路如果不好想的话,我们先来想一条路毕竟这样简单的多
假设我们要找一条路来走,使途经得到的数字和最大,要怎么办呢?
因为我们只有向下和向右两种走法,这就和前两天做的一道题目类似了,它在这里 呃,仅做参考,只是我觉得很类似
方程也就是这样:

num[i][j]=max(num[i-1][j]+a[i][j],num[i][j-1]+a[i][j])

思路很简单,就是看在这一步的基础上看前一步是走的哪里(当然的使总值大的那一个)
好了,一条路走很简单,只看一看动态规划方程就好了,我们重点来看两条路,小女子不才,开始的时候选择了一个很笨的方法:用两个二维数组来分别记录两条路,于是就有了这样的方程:大家不要看,这是错误示范,我只是想记录一下~

for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(i==0&&j==0)
            {
                num[i][j]=arr[i][j];
                mum[i][j]=arr[i][j];
            }

            else if(i==0&&j>0)
            {

                num[i][j]=num[i][j-1]+arr[i][j];
                mum[i][j]=mum[0][0];
            }
            else if(j==0&&i>0)
            {
                mum[i][j]=mum[i-1][j]+arr[i][j];
                num[i][j]=num[0][0];
            }
            else
            {
                p=max(num[i-1][j]+arr[i][j],num[i][j-1]+arr[i][j]);
                q=max(mum[i-1][j]+arr[i][j],mum[i][j-1]+arr[i][j]);
                if(p==num[i-1][j]+arr[i][j]&&q==mum[i-1][j]+arr[i][j])
                {
                    if(p>=q)
                    {
                        num[i][j]=p;
                        mum[i][j]=mum[i][j-1]+arr[i][j];
                    }
                    else
                    {
                        mum[i][j]=q;
                        num[i][j]=num[i][j-1]+arr[i][j];
                    }


                }

                else if(p==num[i][j-1]+arr[i][j]&&q==mum[i][j-1]+arr[i][j])
                    {
                        if(p>=q)
                        {
                            num[i][j]=p;
                            mum[i][j]=mum[i-1][j]+arr[i][j];
                        }
                        else
                        {
                            mum[i][j]=q;
                            num[i][j]=num[i-1][j]+arr[i][j];
                        }
                    }
                    else{
                        num[i][j]=p;
                        mum[i][j]=q;
                    }
            }

        }
    }

很麻烦,而且还不对,所以我们来看正确的思路~
我的错误在于两个二维数组
用两个数组怎么可能好过一个呢,我们直接用一个四维数组它不香嘛!
没错,就是这样:当解决这样的问题时,放在二维空间内通常是不好想的,所以我们要引入高维空间来解题~
我们定义一个四维数组:

int num[n+1][n+1][n+1][n+1];//n+1只是为了不用0

那么num[i][j][p][q]代表什么意思呢?
就是一条路当前走了a[i][j]这个点,另一条路走了a[p][q]这个点,要注意的就是如果这两个点相等怎么办呢?(我们稍后再谈~)
先来看动态规划方程要怎么写~
(1)我们要知道当前有四个状态,他们分别是:

					num[i-1][j][p][q-1]
                    num[i-1][j][p-1][q]
                    num[i][j-1][p][q-1]
                    num[i][j-1][p-1][q]

类比于二维空间(一条路)的两个状态:

					num[i-1][j]+arr[i][j]
					num[i][j-1]+arr[i][j]

我们要做的是什么呢?就是找出我们当前步的上一步走谁这四个状态谁是最优的(也就是取值最大的)
那么,方程就出来了,当当当当~

num[i][j][p][q]=arr[i][j]+arr[p][q]+max(num[i-1][j][p][q-1],max(num[i-1][j][p-1][q],max(num[i][j-1][p][q-1],num[i][j-1][p-1][q])));

方程有了之后就是小细节了,我们遗留的那个问题:如果走了同一个点怎么办?
因为点可以走两次,但数只可以取一次问:为什么?答:看题目
所以我们直接减一次就好了,此时a[rri][j]==arr[p][q]

				if(i==p&&j==q)
                        num[i][j][p][q]-=arr[i][j];

好了,写到这里,就很明了了呢,我们直接看完整代码,当当~

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int n;
    cin>>n;
    int arr[n+1][n+1];
    int p,q,i,j;
    int num[n+1][n+1][n+1][n+1];
    memset(arr,0,sizeof(arr));
    memset(num,0,sizeof(num));
    int a,b,c;
    while(1)
    {
        cin>>a>>b>>c;
        if(a==0&&b==0&&c==0)
            break;
        else{
            arr[a][b]=c;
        }
    }
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            for(p=1;p<=n;p++)
            {
                for(q=1;q<=n;q++)
                {
                    //四种情况:
                    //num[i-1][j][p][q-1]
                    //num[i-1][j][p-1][q]
                    //num[i][j-1][p][q-1]
                    //num[i][j-1][p-1][q]
                    //取max
                    num[i][j][p][q]=max(num[i-1][j][p][q-1],max(num[i-1][j][p-1][q],max(num[i][j-1][p][q-1],num[i][j-1][p-1][q])))+arr[i][j]+arr[p][q];
                    if(i==p&&j==q)
                        num[i][j][p][q]-=arr[i][j];
                }
            }
        }
    }
    cout<<num[n][n][n][n]<<endl;
    return 0;
}

结束,做一个快乐的程序猿~

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值