DP - 线性DP - NOIP2000/2008 - 方格取数 + 传纸条

DP - 线性DP - NOIP2000/2008 - 方格取数 + 传纸条

文章目录

    • DP - 线性DP - NOIP2000/2008 - 方格取数 + 传纸条
      • 1、方格取数 - NOIP 2000
      • 2、传纸条 - NOIP 2008

1、方格取数 - NOIP 2000

设有 N×N 的方格图,我们在其中的某些方格中填入正整数,而其它的方格中则放入数字0。如下图所示:

DP - 线性DP - NOIP2000/2008 - 方格取数 + 传纸条_第1张图片

某人从图中的左上角 A 出发,可以向下行走,也可以向右行走,直到到达右下角的 B 点。

在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。

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

输入格式
第一行为一个整数N,表示 N×N 的方格图。

接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。

一行“0 0 0”表示结束。

输出格式
输出一个整数,表示两条路径上取得的最大的和。

数据范围
N≤10

输入样例:
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
输出样例:
67

分析:

本 题 核 心 问 题 在 于 解 决 “ 每 个 点 的 值 只 加 一 次 ” 的 问 题 。 本题核心问题在于解决“每个点的值只加一次”的问题。

事 实 上 , 每 次 走 一 步 , 要 么 行 增 加 1 , 要 么 列 增 加 1 。 因 此 , 整 个 过 程 可 以 分 为 k 步 , k 从 1 增 加 到 n + m 。 我 们 同 时 进 行 2 个 路 径 的 计 算 , 只 要 确 定 一 条 路 径 的 横 坐 标 , 就 能 确 定 这 条 路 径 的 纵 坐 标 j = k − i 。 若 点 ( i 1 , y 1 ) 与 ( i 2 , y 2 ) 重 合 , 则 i 1 = i 2 。 这 是 因 为 k = i 1 + j 1 = i 2 + j 2 。 事实上,每次走一步,要么行增加1,要么列增加1。\\因此,整个过程可以分为k步,k从1增加到n+m。\\我们同时进行2个路径的计算,只要确定一条路径的横坐标,就能确定这条路径的纵坐标j=k-i。\\若点(i_1,y_1)与(i_2,y_2)重合,则i_1=i_2。这是因为k=i_1+j_1=i_2+j_2。 11kk1n+m2j=ki(i1,y1)(i2,y2)i1=i2k=i1+j1=i2+j2

状 态 表 示 : f [ k ] [ i 1 ] [ i 2 ] : 前 k 步 中 , 第 一 条 路 径 的 横 坐 标 从 1 到 i 1 , 第 二 条 路 径 的 横 坐 标 从 1 到 i 2 的 合 计 最 大 收 益 。 状态表示:f[k][i_1][i_2]:前k步中,第一条路径的横坐标从1到i_1,第二条路径的横坐标从1到i_2的合计最大收益。 :f[k][i1][i2]k1i11i2

状 态 计 算 : 首 先 判 断 i 1 是 否 等 于 i 2 , 若 i 1 ≠ i 2 , 说 明 两 点 不 重 合 , 记 第 k 步 收 益 为 t = w [ i 1 ] [ k − i 1 ] + w [ i 2 ] [ k − i 2 ] 。 若 i 1 = i 2 , 则 记 t = w [ i 1 ] [ k − i 1 ] 。   ① 、 ( i 1 , y 1 ) 由 ( i 1 − 1 , j 1 ) 转 移 而 来 , ( i 2 , y 2 ) 由 ( i 2 − 1 , y 2 ) 转 移 而 来 : f [ k ] [ i 1 ] [ i 2 ] = f [ k − 1 ] [ i 1 − 1 ] [ i 2 − 1 ] + t 。 状态计算:\\首先判断i_1是否等于i_2,\\若i_1≠i_2,说明两点不重合,记第k步收益为t=w[i_1][k-i_1]+w[i_2][k-i_2]。\\若i_1=i_2,则记t=w[i_1][k-i_1]。\\ \ \\①、(i_1,y_1)由(i_1-1,j_1)转移而来,(i_2,y_2)由(i_2-1,y_2)转移而来:f[k][i_1][i_2]=f[k-1][i_1-1][i_2-1]+t。 i1i2i1=i2kt=w[i1][ki1]+w[i2][ki2]i1=i2t=w[i1][ki1] (i1,y1)(i11,j1)(i2,y2)(i21,y2)f[k][i1][i2]=f[k1][i11][i21]+t

② 、 ( i 1 , y 1 ) 由 ( i 1 − 1 , y 1 ) 转 移 而 来 , ( i 2 , y 2 ) 由 ( i 2 , y 2 − 1 ) 转 移 而 来 : f [ k ] [ i 1 ] [ i 2 ] = f [ k − 1 ] [ i 1 − 1 ] [ i 2 ] + t 。 ②、(i_1,y_1)由(i_1-1,y_1)转移而来,(i_2,y_2)由(i_2,y_2-1)转移而来:f[k][i_1][i_2]=f[k-1][i_1-1][i_2]+t。 (i1,y1)(i11,y1)(i2,y2)(i2,y21)f[k][i1][i2]=f[k1][i11][i2]+t

③ 、 ( i 1 , y 1 ) 由 ( i 1 , y 1 − 1 ) 转 移 而 来 , ( i 2 , y 2 ) 由 ( i 2 − 1 , y 2 ) 转 移 而 来 : f [ k ] [ i 1 ] [ i 2 ] = f [ k − 1 ] [ i 1 ] [ i 2 − 1 ] + t 。 ③、(i_1,y_1)由(i_1,y_1-1)转移而来,(i_2,y_2)由(i_2-1,y_2)转移而来:f[k][i_1][i_2]=f[k-1][i_1][i_2-1]+t。 (i1,y1)(i1,y11)(i2,y2)(i21,y2)f[k][i1][i2]=f[k1][i1][i21]+t

④ 、 ( i 1 , y 1 ) 由 ( i 1 , y 1 − 1 ) 转 移 而 来 , ( i 2 , y 2 ) 由 ( i 2 , y 2 − 1 ) 转 移 而 来 : f [ k ] [ i 1 ] [ i 2 ] = f [ k − 1 ] [ i 1 ] [ i 2 ] + t 。 ④、(i_1,y_1)由(i_1,y_1-1)转移而来,(i_2,y_2)由(i_2,y_2-1)转移而来:f[k][i_1][i_2]=f[k-1][i_1][i_2]+t。 (i1,y1)(i1,y11)(i2,y2)(i2,y21)f[k][i1][i2]=f[k1][i1][i2]+t

代码:

#include
#include

using namespace std;

const int N=15;

int n,w[N][N],f[N*2][N][N];

int main()
{
    cin>>n;
    
    int x,y,v;
    while(cin>>x>>y>>v,x||y||v) w[x][y]=v;
    
    for(int k=2;k<=n*2;k++)
        for(int i1=1;i1<=n;i1++)
            for(int i2=1;i2<=n;i2++)
            {
                int j1=k-i1,j2=k-i2;
                if(j1>=1&&j1<=n&&j2>=1&&j2<=n)
                {
                    int t=w[i1][j1];
                    if(i1!=i2) t+=w[i2][j2];
                    int &x=f[k][i1][i2];
                    x=max(x,f[k-1][i1-1][i2-1]+t);
                    x=max(x,f[k-1][i1-1][i2]+t);
                    x=max(x,f[k-1][i1][i2-1]+t);
                    x=max(x,f[k-1][i1][i2]+t);
                }
            }
            
    cout<<f[2*n][n][n]<<endl;
    
    return 0;
}

2、传纸条 - NOIP 2008

小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。

一次素质拓展活动中,班上同学安排坐成一个 m 行 n 列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。

幸运的是,他们可以通过传纸条来进行交流。

纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n)。

从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。

在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。

班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙,反之亦然。

还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用0表示),可以用一个0-100的自然数来表示,数越大表示越好心。

小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度之和最大。

现在,请你帮助小渊和小轩找到这样的两条路径。

输入格式
第一行有2个用空格隔开的整数 m 和 n,表示学生矩阵有 m 行 n 列。

接下来的 m 行是一个 m∗n 的矩阵,矩阵中第 i 行 j 列的整数表示坐在第 i 行 j 列的学生的好心程度,每行的 n 个整数之间用空格隔开。

输出格式
输出一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。

数据范围
1≤n,m≤50

输入样例:
3 3
0 3 9
2 8 5
5 7 0
输出样例:
34

分析:

题 中 说 从 ( 1 , 1 ) 到 ( n , m ) 同 时 从 ( n , m ) 到 ( 1 , 1 ) , 求 合 计 最 大 收 益 。 其 实 正 向 反 向 是 不 影 响 计 算 过 程 的 , 代 码 同 上 。 题中说从(1,1)到(n,m)同时从(n,m)到(1,1),求合计最大收益。\\其实正向反向是不影响计算过程的,代码同上。 (1,1)(n,m)(n,m)(1,1)

代码:

#include
#include

using namespace std;

const int N=55;

int n,m,w[N][N],f[2*N][N][N];

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) 
        for(int j=1;j<=m;j++) 
            cin>>w[i][j];
            
    for(int k=2;k<=n+m;k++)
        for(int i1=1;i1<=n;i1++)
            for(int i2=1;i2<=n;i2++)
            {
                int j1=k-i1,j2=k-i2;
                if(j1>=1&&j1<=m&&j2>=1&&j2<=m)
                {
                    int t=w[i1][j1];
                    if(i1!=i2) t+=w[i2][j2];
                    int &x=f[k][i1][i2];
                    x=max(x,f[k-1][i1-1][i2-1]+t);
                    x=max(x,f[k-1][i1-1][i2]+t);
                    x=max(x,f[k-1][i1][i2-1]+t);
                    x=max(x,f[k-1][i1][i2]+t);
                }
            }

    cout<<f[n+m][n][n]<<endl;
    
    return 0;
}

你可能感兴趣的:(DP)