【DP】-DLUToj-1268-DP走地图

题目链接:http://acm.dlut.edu.cn/problem.php?id=1268

题目描述:

给出一个数字构成的矩阵,小明可以把第一排的任一点作为起点,把地图中的任意一点作为终点进行移动,移动方式仅限向左一步,向右一步或向下一步,也就是说不能往上走,小明每路过一个点,就会捡起这个点上的数字加到自己的得分里,每个点的分数只能被捡起一次,也就是说捡完之后改点变成0,问小明最大得分。

解题思路:

一道DP,看似背包,不是背包,是道普通而纠结的DP,想法不难,但以我目前的水平在比赛的真实环境下真的做不出。

如何DP。。听我慢慢道来

我讲的可能没按解题的思考顺序来...表达能力比较差~谅解哈

首先首先需要明白这样一件事,在同一行里,从 i --> j 得到的分数与从 j --> i 得到的分数与 i --> j --> i 得到的分数都是一样的。——结论①

于是我们可以产生这样一种想法,只考虑一行里的情况——我们也许能求出把某个点作为终点能得到的最大分数。

起点是第一排,那我们就先考虑一下第一排的事。

根据经验我们不难想到这样两个dp数组,ldp和rdp。

ldp【i】是从左侧某点起连续到 i 点能得到的最大分数,根据结论①也可以理解成是,以这点 i 为起点只向左走能得到的最大值。

ldp【i】= max( ldp【i -1】+ arr【i】,arr【i】)

同理,rdp就是换成右边的,差不多的东西。

下面举个栗子

 i0123456
ldp123-2123
arr111-5111
rdp321-2321
temp3331333
temp是干什么的呢?temp【i】表示这一行里把 i 作为终点能取到的最大值。

求法如下:

temp【i】= max(arr【i】,arr【i】+ ldp【i-1】,arr【i】+rdp【i+1】,arr【i】+ ldp【i-1】+ rdp【i+1】)  //证明略

但一定要注意!这是只考虑这一行的时候这么干,我们只对第一行的最终dp值dp【i】【j】(dp【i】【j】是以【i】【j】为终点能得到的最大分数)是这么求出来的。而求其他行的时候,因为其他行是从上一行搞下来的,所以上一行的dp值会对这一行产生影响,只考虑这一行的情况是行不通的,我们要遍历一遍求最优解。

怎么求第一行以外行的dp值呢

在求dp【i】【j】的时候设一个掉落点k,表示是从【i-1】【k】位置掉落到第 i 行的,随后移动到【i】【j】点位置。

以 k 在 j 左侧为例,如下图所示:


显然为了让dp【i】【j】最大,dp【i】【j】= ldp【k】+num【i】【k+1】+...+ num【i】【j-1】+rdp【j】

k = j 和 k > j 的情况另外写一下,大同小异。

如此遍历所有的 k 就可以求出dp【i】【j】的最大值。

求数组里区间和只要预处理一下就可以把时间复杂度降到o(1)。所以按照以上算法的复杂度是o(n*m*m),按照题目数据范围,最大是1000*50*50,给了足足5s,肯定能过。而比赛的时候正是因为一直在想o(n*m)的才把自己作死。

反思总结:

你说。。这题难吗。也不算很难,图书馆里静静地审视一下,看一下数据范围,就过了,比赛时候就没过。确实,这道dp的求解方式比较纠结,折腾了好几遍,还是有一定难度的。不过比赛时候我们没有看清题目也没有完全验证自己dp公式的对错就开始写了,这是个败笔,写dp最恐怖的是公式本身严重错误,却一直没发现,但凡能用dp解决的问题,其正确公式一定是显而易见一想就绝对对的,而不可能是想起来好像对,真正证明又模棱两可的!我觉得这道题出的非常好,很考验代码能力和比赛素质,比赛没做出来是水平不够,唉~这点认输。真正看来,并不是很难的题,说是中等题恐怕都要被大神嘲笑,完全有能力做出来,通过这题也算是增长了水平,以后再遇到这种“水题”,一定要做出来!

AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>
 
using namespace std;
typedef long long ll;
 
int N,M;
ll num[1200][120];
ll sum[1200][120];
ll temp[1200][120];
ll ldp[120];
ll rdp[120];
 
ll MMax(ll a,ll b,ll c,ll d)
{
    ll x=max(a,b);
    ll y=max(c,d);
    return x>y?x:y;
}
 
int main()
{
    //freopen("input.txt","r",stdin);
    int T,i,j,k;
    cin>>T;
    while(T--)
    {
        memset(num,0,sizeof(num));
        memset(temp,0,sizeof(temp));
        cin>>N>>M;
        for(i=0;i<N;i++)
            for(j=0;j<M;j++)
            {
                cin>>num[i][j];
                if(j==0)
                    sum[i][0]=num[i][0];
                else
                    sum[i][j]=num[i][j]+sum[i][j-1];
            }
        for(i=0;i<N;i++)
        {
            memset(ldp,0,sizeof(ldp));
            memset(rdp,0,sizeof(rdp));
            ldp[0]=num[i][0];
            rdp[M-1]=num[i][M-1];
            for(j=1;j<M;j++)
                ldp[j]=max(ldp[j-1]+num[i][j],num[i][j]);
            for(j=M-2;j>=0;j--)
                rdp[j]=max(rdp[j+1]+num[i][j],num[i][j]);
            if(i==0)
            {
                temp[i][0]=max(num[i][0]+rdp[1],num[i][0]);
                if(M>=2)
                    temp[i][M-1]=max(num[i][M-1]+ldp[M-2],num[i][M-1]);
                for(j=1;j<M-1;j++)
                    temp[i][j]=MMax(ldp[j-1]+num[i][j],num[i][j]+rdp[j+1],ldp[j-1]+num[i][j]+rdp[j+1],num[i][j]);
            }
            else
            {
                for(j=0;j<M;j++)
                {
                    ll maxtemp;
                    if(j==0)
                        maxtemp=max(temp[i-1][j]+num[i][j],temp[i-1][j]+num[i][j]+rdp[j+1]);
                    else
                        maxtemp=MMax(temp[i-1][j]+num[i][j],temp[i-1][j]+num[i][j]+ldp[j-1],temp[i-1][j]+num[i][j]+rdp[j+1],temp[i-1][j]+num[i][j]+ldp[j-1]+rdp[j+1]);
                    for(k=0;k<j;k++)
                        maxtemp=max(temp[i-1][k]+ldp[k]+sum[i][j-1]-sum[i][k]+rdp[j],maxtemp);
                    for(k=j+1;k<M;k++)
                        maxtemp=max(temp[i-1][k]+ldp[j]+sum[i][k-1]-sum[i][j]+rdp[k],maxtemp);
                    temp[i][j]=maxtemp;
                }
            }
        }
 
/*
        for(i=0;i<N;i++)
        {
            for(j=0;j<M;j++)
                cout<<temp[i][j]<<" ";
            cout<<endl;
        }
*/
        ll ans=temp[0][0];
        for(i=0;i<N;i++)
            for(j=0;j<M;j++)
                ans=max(ans,temp[i][j]);
        cout<<ans<<endl;
        //cout<<endl;
    }
    return 0;
}
AC截图:


//。。。。才跑了472ms = =

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值