NOIP 2008 传纸条

这道题目提供三维优化的题解和没有优化的题解

不多说,先看代码

 1 /*
 2     首先如果只有一个人,状态很好转移,用f[i][j]表示在位置(i,j)的最优值,那么f[i][j]=max(f[i-1][j],f[i][j-1)+a[i][j];
 3     现在又两个人,我们用f[i][j][k][t]表示位置(i,j)(k,t)的最优值,虽然两个人一个是左上角开始,一个是从右下角开始,
 4     但是我们也可以转化成都从左上角开始,那么方程就可以得到
 5     f[i][j][k][t]=max(f[i-1][j][k-1][t],f[i-1][j][k][t-1],f[i][j-1][k-1][t],f[i][j-1][k][t-1])+dis;
 6     这里还有一个dis值没有解决,题目的特殊性,(i,j)和(k,t)不能是同一个点,那么如果走到同一点,我们只加一次就避免了这个问题,
 7     所以是同一点就+a[i][j],不是同一点就+a[i][j]+a[k][t]
 8 */
 9 #include<cstdio>
10 #define MAXN 60
11 #define max(a,b) ((a)>(b)?(a):(b))
12 
13 int n,m,a[MAXN][MAXN];
14 int f[MAXN][MAXN][MAXN][MAXN];
15 
16 int main()
17 {
18     scanf("%d%d",&n,&m);
19     for(int i=1;i<=n;i++)
20         for(int j=1;j<=m;j++)
21             scanf("%d",&a[i][j]);
22     for(int i=1;i<=n;i++)
23         for(int j=1;j<=m;j++)
24             for(int k=1;k<=n;k++)
25                 for(int t=1;t<=m;t++)
26                 {
27                     f[i][j][k][t] = max( f[i][j][k][t], f[i-1][j][k-1][t] );
28                     f[i][j][k][t] = max( f[i][j][k][t], f[i-1][j][k][t-1] );
29                     f[i][j][k][t] = max( f[i][j][k][t], f[i][j-1][k-1][t] );
30                     f[i][j][k][t] = max( f[i][j][k][t], f[i][j-1][k][t-1] );
31                     f[i][j][k][t]+=a[i][j];
32                     if(i!=k || j!=t)f[i][j][k][t]+=a[k][t];
33                 }
34     printf("%d",f[n][m][n][m]);
35     return 0;
36 }

然后送上三维优化的题解

/*
    三维优化后的题解:
    题目大意:给定一个M*N的数组,每个数组都有一个值,求出两条从(1,1)到(M,N)的路径,在满足
    ①路径不相交
    ②从(1,1)出发的路径只能向右和向下移动,从(N,M)出发的路径只能向左向上移动的前提下路径上所经过的元素的值的和最大。
    对于某一条路径,如果我将它投影到边界上的话,可以发现它的长度一定是n+m-1,因此无论怎么走路径的长度均为n+m-1。
    因此用i来记录当前走的总步数,j记录第一条路径向右走的次数,k记录第二条路径向左走的次数。(假设了起点是第1步)
    若设sum为当前已经走到的点a[j][i-j+1]和a[k][i-k+1]的权值和,
    则f[i][j][k]=max(f[i-1][j][k-1],f[i-1][j-1][k-1],f[i-1][j-1][k],f[i-1][j][k])+sum。枚举i,j,k即可。
*/
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;

const int Max=50+2;
int a[Max][Max],f[2*Max][Max][Max],m,n;

int main()
{
    cin >> m >> n;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            cin >> a[i][j];

    for(int i=1,sum=0;i<=m+n-1;i++)        //i表示A,B两个人同时移动,统一的路径步数
        for(int j=1;j<=min(i,m);j++)       //j表示走i步时,A的横坐标
            for(int k=1;k<=min(i,m);k++)   //k表示走i步时,B的横坐标
            {
                if(j==k) continue;         //走到同一位置,不允许出现的情况
                sum=a[j][i-j+1]+a[k][i-k+1];

                f[i][j][k]=max(f[i][j][k],f[i-1][j][k-1]+sum);
                f[i][j][k]=max(f[i][j][k],f[i-1][j-1][k]+sum);
                f[i][j][k]=max(f[i][j][k],f[i-1][j-1][k-1]+sum);
                f[i][j][k]=max(f[i][j][k],f[i-1][j][k]+sum);
            }
    //起点记作第一步,由起点(1,1)->(M,N)需要的总步数一定是M+N-1,
    //因为最终不可能出现f[m+n-1][m][m]的状态(显然计算过程中判断到最终同一位置,会continue),矛盾
    //因此实际上最终的状态只要达到,A到达终点的上方,B到达终点的左边,下一步结果不是显然的吗? 于是结果出来了。。。
    cout << f[m+n-2][m-1][m] << endl;   

    return 0;
}
原文地址:https://www.cnblogs.com/zi-nai-boboyang/p/11437058.html