老师丧尽天良!!!!!
这是老师给我们的普及组的模拟题!!
尼谷上难度:普及+/提高
考试鬼能想得出来啊!!!
虽然当时我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.