NIOP提高组 尼谷P1006 传纸条 详解

老师丧尽天良!!!!!

这是老师给我们的普及组的模拟题!!

体面在这里!

尼谷上难度:普及+/提高

考试鬼能想得出来啊!!!

虽然当时我AK了。。。。(捂头)
附上考试时的代码:

#include<bits/stdc++.h>
#define qwq int
#define Max 55 
using namespace std;
qwq a[Max][Max];
qwq f[Max][Max][Max][Max];

qwq main()
{
    qwq n,m;
    scanf("%d%d",&n,&m);
    for(qwq i = 1;i <= n;++ i)
        for(qwq j = 1;j <= m;++ j)
            scanf("%d",&a[i][j]);
    for(qwq i = 1;i <= n;++ i){
        for(qwq j = 1;j <= m;++ j){
            for(qwq k = 1;k <= n;++ k){
                qwq l = i + j - k;
                if(l <= j)
                    break;
                f[i][j][k][l] = max(max(f[i][j - 1][k - 1][l],f[i][j - 1][k][l - 1]),max(f[i - 1][j][k - 1][l],f[i - 1][j][k][l - 1]));
                f[i][j][k][l] += a[i][j] + a[k][l];
            }
        }
    }
    cout << f[n][m - 1][n - 1][m]  << endl;
    return 0;
}

简练,精辟。(哈哈哈哈哈)
当时我就是把它当个正常DP做,于是就有了上面的代码

后来想想,好像当时的思路及代码都能优化啊啊啊。

首先,是空间复杂度的问题。

大家看,我原先写的存储步数的数组 f[i][j][k][q],是个四维数组,很容易算出,站的空间为55 * 55 * 55 * 55=9150625。这还是题目组良心,最大只有50,来个委(e)婉(xin)点的,随便搞个500等3位数就废了。

后来我发现,可以用三维数组存啊。数组叫f[i][j][k]。具体为什么,未来我说完思路后会讲。

好的,接下来就说说思路的优化。

完全可以这么想,求从给定的起点出发走到指定位置的两条最短严格不相交路线。

那么特别显然,转移方程是 f[i][j][k][l]=max( f[i][j-1][k-1][l] , f[i-1][j][k][l-1] , f[i][j-1][k][l-1] , f[i-1][j][k-1][l] )+a[i][j]+a[k][l]。

(本转移方程是四维DP的)

要小心l的枚举范围,应该是从j+1到m,只有这样,在枚举第二条路的时候可以控制下标的l不会和j有相等的可能,这样可以保证两条路一定不相交(想一想,为什么)

好,讲完思路的优化,我们就可以接着3维数组讲了。
三维和四维的思考角度是有所不同的。

四维: f[i][j][k][l]为从小渊传到小轩的纸条到达(i,j),从小轩传给小渊的纸条到达(k,l)的路径上取得的最大的好心程度和。

三维: 仔细观察,我们不难发现一个规律,对于每次转移,这两位同学的纸条走的步数总是相等的,也就是应该总有i+j = k+l = step,我们从这里考虑入手,简化一下那个方程。

我们枚举走的步数,同时枚举第一个人和第二个人的横坐标或者纵坐标,对,只枚举一个就好,另一个可以算出来。

我枚举的是横坐标。

但这样做第一维(也就是枚举步数那一维)要开两倍大小(步数最大有n+m-1),并且需要加入判断重合操作。

优化之后速度和空间比上一个好很多,因为它的时间复杂度是O(n^2*(n+m)).

优化后代码奉上:

#include <bits/stdc++.h>
#define qwq int
#define Max 55
using namespace std;
qwq f[2 * Max][Max][Max];
qwq WO__SHI__CAI__JI[Max][Max];
qwq n,m;
qwq max_ele(qwq a,qwq b,qwq c,qwq d){
    if (b>a)
        a = b;
    if (c>a)
        a = c;
    if (d>a)
        a = d;
    return a;
}

qwq main(){
    cin >> n >> m;
    for (qwq i=1;i<=n;i++)
        for (qwq j=1;j<=m;j++)
            cin >> WO__SHI__CAI__JI[i][j];
    for (qwq k=1;k<=n+m-1;k++)
        for (qwq i=1;i<=n;i++)
            for (qwq j=1;j<=n;j++){
                if (k-i+1<1 || k-j+1<1) //这里是判断纵坐标的合法性,若纵坐标不合法,则跳过这一循环
                    continue;
                 f[k][i][j] = max_ele(f[k-1][i][j],f[k-1][i-1][j-1],f[k-1][i][j-1],f[k-1][i-1][j]) + WO__SHI__CAI__JI[i][k-i+1] + WO__SHI__CAI__JI[j][k-j+1]; //很SAO的状态转移方程(莫名骄傲)
                if (i==j) 
                    f[k][i][j]-=WO__SHI__CAI__JI[i][k-i+1];//前面说的点不能重合
            }

    cout << f[n+m-1][n][n] << endl;
    return 0;
}

绝壁是一个很好的解法,不信你去“谷”, " 本通",“???io”上去提交,错了我螺旋倒立360度旋转飞天边跳HOP边 eat shit >>>>>>>>>%&(&#%@(&@&%$#%#&%)&

qiu zan .Thanks.

The end

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值