NOIP2000 方格取数(动态规划 详细解答)

这里我们按照一定的流程来做

也就是把思维过程一步步写下来

很多DP题都可以按照这个思维来做

题目描述

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

A
 0  0  0  0  0  0  0  0
 0  0 13  0  0  6  0  0
 0  0  0  0  7  0  0  0
 0  0  0 14  0  0  0  0
 0 21  0  0  0  4  0  0
 0  0 15  0  0  0  0  0
 0 14  0  0  0  0  0  0
 0  0  0  0  0  0  0  0
                        B

某人从图的左上角的A点出发,可以向下行走,也可以向右走,直到到达右下角的B
点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从A点到B点共走两次,试找出 2 2 条这样的路径,使得取得的数之和为最大。
输入输出格式
输入格式:
输入的第一行为一个整数N(表示 NN N ∗ N 的方格图),接下来的每行有三个整数,前两个
表示位置,第三个数为该位置上所放的数。一行单独的0表示输入结束。
输出格式:
只需输出一个整数,表示2条路径上取得的最大的和。

题解

典型的四维DP模板。
同时开始两个进程。

先写一个DFS

既然题目中是说了,要走两次,那么我们就假设这两条路径是同时走的。那我们不难写出这样一个dfs。
(伪代码)

int ans,a[][];//a数组代表每个点的数
void dfs(int x1,int y1,int x2,int y2,int len)
{
    if(两个坐标都到达了终点)
    {
        ans=max(ans,len);
        return;
    }
    if(两个坐标至少有一个超出了边界)
    {
        return;
    }
    int now=len+a[x1+1][y1]+a[x2+1][y2];
    if(x1+1==x2+1&&y1==y2)
    now-=a[x1+1][y1]
    dfs(x1+1,y1,x2+1,y2,now);

    now=len+a[x1][y1+1]+a[x2+1][y2];
    if(x1==x2+1&&y1+1==y2)
    now-=a[x1][y1+1];
    dfs(x1,y1+1,x2+1,y2,now);

    now=len+a[x1+1][y1]+a[x2][y2+1];
    if(x1+1==x2&&y1==y2+1)
    now-=a[x1+1][y1];
    dfs(x1+1,y2,x2,y2+1,now);

    now=len+a[x1][y1+1]+a[x2][y2+1];
    if(x1==x2&&y1+1==y2+1)
    now-=a[x1][y1+1];
    dfs(x1,y1+1,x2,y2+1,now);

}

设出状态

我们根据dfs调用的参数,设一下状态。
如果有k个参数,那么一般k-1个参数做下标,1个参数做值。
这里是两个坐标值做下标,总路程做值。
也就是

f[x1][y1][x2][y2]=len f [ x 1 ] [ y 1 ] [ x 2 ] [ y 2 ] = l e n

代表一个条路径到 x1,y1 x 1 , y 1 ,另外一条路径到 x2,y2 x 2 , y 2 的时候的最短路径。

写成记忆化搜索

int a[][];//a数组代表每个点的数
int dis[][][][];//没有访问过默认-1
int dfs(int x1,int y1,int x2,int y2,int len)
{
    if(两个坐标至少有一个超出了边界)
    {
        return -INF;
    }
    if(dis[x1][y1][x2][y2]!=-1)
    {
        return dis[x1][y1][x2][y2];
    }
    if(两个坐标都到达了终点)
    {
        return len;
    }
    int now=len+a[x1+1][y1]+a[x2+1][y2];
    if(x1+1==x2+1&&y1==y2)
    now-=a[x1+1][y1]
    int t1=dfs(x1+1,y1,x2+1,y2,now);

    now=len+a[x1][y1+1]+a[x2+1][y2];
    if(x1==x2+1&&y1+1==y2)
    now-=a[x1][y1+1];
    int t2=dfs(x1,y1+1,x2+1,y2,now);

    now=len+a[x1+1][y1]+a[x2][y2+1];
    if(x1+1==x2&&y1==y2+1)
    now-=a[x1+1][y1];
    int t3=dfs(x1+1,y2,x2,y2+1,now);

    now=len+a[x1][y1+1]+a[x2][y2+1];
    if(x1==x2&&y1+1==y2+1)
    now-=a[x1][y1+1];
    int t4=dfs(x1,y1+1,x2,y2+1,now);
    return max(t1,max(t2,max(t3,t4)));
}

记忆化改循环

既然有四个参数,那么就四重循环。
这里的搜索顺序没有影响。所以循环方向没有影响。
直接递推求解。

int f[][][][];
int a[][];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
for(int l=1;l<=n;l++)
{
    f[i][j][k][l]=max()+a[i][j]+a[k][l];
    if(i==k&&j==l)//如果同一个点被同时经过了。
    f[i][j][k][l]-=a[i][j];
}

你可能感兴趣的:(dp,题解,模板)