【NOIP2000】T4方格取数

来源:互联网 发布:知乎周刊第一本 编辑:程序博客网 时间:2024/05/17 07:16
先从动规刷起 ,做了这么一道题,2000年的老题,提高组第四题
//////////////////////////////////////////////////////////

题目描述 Description

设有N*N的方格图(N<=10,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。如下图所示(见样例):

某人从图的左上角的A 点出发,可以向下行走,也可以向右走,直到到达右下角的B点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。

此人从A点到B 点共走两次,试找出2条这样的路径,使得取得的数之和为最大。

http://codevs.cn/media/image/problem/1043.png?_=4540186

输入描述 Input Description

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

输出描述 Output Description

    只需输出一个整数,表示2条路径上取得的最大的和。

样例输入 Sample Input

      8

      2  3  13

      2  6   6

      3  5   7

      4  4  14

      5  2  21

      5  6   4

      6 3  15

      7 2  14

      0 0  0

样例输出 Sample Output

      67

数据范围及提示 Data Size & Hint
如描述

 ////////////////////////////////////////////////////////////////////////////////////

 题目直接限制了只能向下或向右走,换句话说就是每个点只能由上或由左走到。因此,如果只考虑一条路径,还是比较直观就能想明白:

设F[i][j]表示走到i行j列时的最大值
则F[i][j]=max{F[i-1][j],F[i][j-1]} +a[i][j]//F[i][j]只能由这两格转移来
只考虑一条路径就可以这么写:
   
for(i=1;i<=n;i++)  for(j=1;j<=n;j++)    if(f[i-1][j]>f[i][j-1])f[i][j]=f[i-1][j]+map[i][j];    else f[i][j]=f[i][j-1]+map[i][j];

虽然,题目要求又多了一条路径,但是如果沿着这个思路,也就不难想了,只是需要四维数组,分别记下两条路径。而下一个状态,应该是以四种状态中的最大值转移得来。由于题目限制n<=10,于是就可以不用犹豫使用这样的四种循环动规求解了,列出动态转移方程
设F[i][j][k][l]表示第一条路径走到i行j列,第二条路径走到k行l列时的最大值
则F[i][j][k][l]=max{F[i-1][j][k-1][l],F[i][j-1][k-1][l],F[i-1][j][k][l-1],F[i][j-1][k][l-1]}+a[i][j]+a[k][l]
//分别是第一条路径的两种和第二条路径的两种,乘法原理
因此,最后的写法就是个四重循环O(n^4)//n<=10
for(i=1;i<=n;i++)  for(j=1;j<=n;j++)   for(k=1;k<=n;k++)     for(l=1;l<=n;l++){     f[i][j][k][l]=max(f[i-1][j][k-1][l],f[i][j-1][k-1][l],     f[i-1][j][k][l-1],f[i][j-1][k][l-1])+map[i][j]+map[k][l];     if(i==k&&j==l)f[i][j][k][l]-=map[i][j];    }


这里还有一个容易忽视的细节,i,j,k,l重合时,a[i][j]只能被加一次//边界条件

这题尽管不是很难理解,但利用了n<=10这一条件,用四维数组DP的做法还是很巧妙。


 ////////////////////////////////////////////////////////////////////////////////////

居然还有四维压三维的做法

之前的F[i][j][k][l]四维做法,会发现有许多冗余计算:对于每一组路径上各取一点的所有组合都会被重复计算。

我们可以发现因为只能向下或向右,两条路径的长度是确定的,而设共走了k步(以左上角为第一步),对于每一个k,点(i,j)的坐标都有i=k-j+1,因此就可以以一个k代替原来的两个横坐标(或纵坐标)。

于是可以进行三维的优化:

设F[i][j][k]表示第一条路径走到第i列,第二条路径走到第j列,共走了k步时的最大值

这样,动态转移方程就是:

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

//F[i][j][k]由上一步k-1的四个点的最大值转移,四个点与四维做法相同

还有一点是,由于记录的是步数,对应步数下的i,j取值范围改变,应为[1,k](k<=n),[k-n+1,n](k>n)

所以,最后的代码O(n^3):

f[1][1][1]=map[1][1];for(k=2;k<=2*n-1;k++)  for(i=max(1,k-n+1);i<=min(n,k);i++)    for(j=max(1,k-n+1);j<=min(n,k);j++){      f[i][j][k]=max(max(f[i-1][j][k-1],f[i][j-1][k-1]),      max(f[i][j][k-1],f[i-1][j-1][k-1]))+map[i][k-i+1]+map[j][k-j+1];      if(i==j)f[i][j][k]-=map[i][k-i+1];    }printf("%d",f[n][n][2*n-1]);

这就很妙了( ̄▽ ̄)~*

原创粉丝点击