我的DP训练计划:三取方格数

题面:

背景

JerryZhou同学经常改编习题给自己做。

这天,他又改编了一题。。。。。

描述

设有N*N的方格图,我们将其中的某些方格填入正整数,
而其他的方格中放入0。

某人从图得左上角出发,可以向下走,也可以向右走,直到到达右下角。

在走过的路上,他取走了方格中的数。(取走后方格中数字变为0)
此人从左上角到右下角共走3次,试找出3条路径,使得取得的数总和最大。

格式

输入格式

第一行:N (4<=N<=20)
接下来一个N*N的矩阵,矩阵中每个元素不超过80,不小于0

输出格式

一行,表示最大的总和。

听...听说是一类经典DP...

反正我是不会...

一开始我的思路就很一根筋:直接按走一次做,然后重复三遍

然后,然后就听取WA声一片...

反正最后也没想出来,是看的题解的思路...呜呜呜我太菜了

有一个比较重要的结论是:

  在矩阵中,如果只能向下走或者向右走,那么,知道起点的前提下,步数,行数,列数,知道其中两个能推第三个

  并且,走到同一位置的两个点,必定花费了相同的步数

这些结论支撑起了我们的思路:让三个点同时走

下面试着构建状态:f[x][i][j][k],代表走了x步,i,j,k代表三个人分别走到了第i行,第j行,第k行。

其实也就等价于走到了第i行第x-i+2列,第j行第x-j+2列,第k行第x-k+2列。

 对于每一个状态,这个状态之前最多会有8种状态(每个点分别从左边和从上边过来),也就是说,要从这8个状态中选一个最好的状态来更新当前状态。

写成方程就是:

f[x,i,j,k]=max(f[x-1,i-1,j,k],f[x-1,i,j-1,k],f[x-1,i,j,k-1], //其中一个从上边来
          f[x-1,i-1,j-1,k],f[x-1,i-1,j,k-1],f[x-1,i,j-1,k-1], //其中两个从上边来
          f[x-1,i-1,j-1,k-1], //三个都从上边来
          f[x-1,i,j,k]) //三个都不从上边来
          +add //对应需要加上的 一个/两个/三个 点的值

(代码里没特判边界是因为无效的前一个状态对应的f是0,也就是说,如果有任何一个合法的上一个状态都要比它大)

由于格子里的数被取走就没了,所以要判断有没有任意两/三个点走到了同一个格子,不要加多了。(我就在这里犯傻了...三个位置两两不同是:i!=j&&j!=k&&i!=k,我当成了i!=j&&j!=k就行...)

代码如下:

#include <cstdio>
#include <cstdlib>
int N;
int v[21][21];
int f[41][21][21][21];
int max(int a,int b){return a>b?a:b;}
void dp(){
    for(int x=1;x<=2*N-2;x++){
        for(int i=1;i<=x+1&&i<=N;i++){
            for(int j=1;j<=x+1&&j<=N;j++){
                for(int k=1;k<=x+1&&k<=N;k++){
                    int add;
                    if(i==j&&j==k)add=v[i][x-i+2];
                    if(i==j&&j!=k)add=v[i][x-i+2]+v[k][x-k+2];
                    if(i!=j&&j==k)add=v[i][x-i+2]+v[j][x-j+2];
                    if(i==k&&j!=k)add=v[i][x-i+2]+v[j][x-j+2];
                    if(i!=j&&j!=k&&i!=k)add=v[i][x-i+2]+v[j][x-j+2]+v[k][x-k+2];
                    f[x][i][j][k]=max(f[x-1][i-1][j][k],max(f[x-1][i][j-1][k],max(f[x-1][i][j][k-1],
                    max(f[x-1][i-1][j-1][k],max(f[x-1][i-1][j][k-1],max(f[x-1][i][j-1][k-1]
                    ,max(f[x-1][i-1][j-1][k-1],f[x-1][i][j][k])))))))+add;
                }
            }
        }
    }
}
int main(){
    scanf("%d",&N);
    for(int i=1;i<=N;i++){
        for(int j=1;j<=N;j++)
            scanf("%d",&v[i][j]);
    }
    f[0][1][1][1]=v[1][1];
    dp();
    printf("%d",f[2*N-2][N][N][N]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Zarax/p/12041655.html
今日推荐