1.1 数字三角形模型

AcWing 1015. 摘花生

分析

在这里插入图片描述

code

#include <iostream>
#include <cstring>
using namespace std;
const int N = 110;
int f[N][N], w[N][N];
int n, m;

int main(){
    int T;
    cin >> T;
    while (T -- ){
        memset(f, 0, sizeof f);
        cin >> n >> m;
        for (int i = 1; i <= n; i ++ )
            for  (int j = 1; j <= m; j ++ )
                cin >> w[i][j];
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= m; j ++ )  
                f[i][j] += max(f[i - 1][j], f[i][j - 1]) + w[i][j];
        cout << f[n][m] << endl;
    }
    return 0;
}

AcWing 1018. 最低通行费

分析

必须在2N- 1的的时间内穿越出去, 意味着只能往右边和往下走

code

#include <iostream>
using namespace std;
const int N = 110;
int w[N][N], f[N][N];
int n;

int main(){
    cin >> n;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            cin >> w[i][j];
            
    for (int i = 1 ; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            if (i == 1 && j == 1) f[i][j] = w[i][j];
            else if (i == 1) f[i][j] = f[i][j - 1] + w[i][j];
            else if (j == 1) f[i][j] = f[i - 1][j] + w[i][j];
            else f[i][j] = min(f[i - 1][j], f[i][j - 1]) + w[i][j];
    cout << f[n][n] << endl;
    return 0;
}

AcWing 1027. 方格取数

分析

参考QinLaoDeMaChu的博客
本题的不同之处在于总共走两次。走两次可以是先后走两次,可以是同时并行的走两条路径,我们的方法是并行走两条。
走两次:
f[i1,j1,i2,j2] 表示所有从(1, 1), (1, 1) 分别走到 (i1, j1), (i2, j2) 的路径的最大值。
如何处理 “同一个格子不能重复选取”?
只有在 i1+j1 = i2+j2 时,两条路径的格子才可能重合。
有了这个等价关系,状态表示就可以优化为三维,
k 表示两条路径当前走到的格子横纵坐标之和, k = i1+j1 = i2 + j2 。
f[k, i1, i2] 表示 所有从(1, 1), (1, 1) 分别走到 (i1, k-i1), (i2, k-i2) 的路径的最大值。

集合如何划分呢?
我们只考虑最后一步,每条路径都有向下,向右两种选择,两条路径共有四种情况。
在这里插入图片描述

code

#include <iostream>
#include <cstring>
using namespace std;
const int N = 11;
int w[N][N], f[2* N][N][N];
int n;

int main(){
    cin >> n;
    int x, y, c;
    while (scanf("%d%d%d", &x, &y, &c), x || y || c){
        w[x][y] = c;
    }
    
    for (int k = 2; k <= 2 * n; 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];
                    f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1 - 1][i2 - 1] + t);
                    f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1 - 1][i2] + t);
                    f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1][i2 - 1] + t);
                    f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1][i2] + t);
                }
            }
    cout << f[2 * n][n][n] << endl;
    return 0;
}

AcWing 275. 传纸条

分析

同上题, 此题做多走n + m

code(仿照上题)

#include <iostream>
#include <cstring>
using namespace std;
const int N = 55;
int w[N][N], f[2* N][N][N];
int n, m;

void print(){
    for (int k = 2; k <= n + m; k ++ )
        for (int i1 = 1; i1 <= n; i1 ++ )
            for (int i2 = 1; i2 <= n; i2 ++ )
                printf("f[%d][%d][%d] = %d \n", k, i1, i2, f[k][i1][i2]);
}
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];
                    f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1 - 1][i2 - 1] + t);
                    f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1 - 1][i2] + t);
                    f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1][i2 - 1] + t);
                    f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1][i2] + t);
                }
            }
            
    // print();
    cout << f[n + m][n][n] << endl;
    return 0;
}

code(代码中间部分做优化)

根据 1 ≤ j 1 = k − i 1 ≤ m ,   1 ≤ j 2 = k − i 2 ≤ m 1 \leq j1 = k - i1 \leq m,\ 1\leq j2 = k - i2 \leq m 1j1=ki1m, 1j2=ki2m, 可以求得i1, i2的边界
代码中间的循环, 做了优化

#include <iostream>
using namespace std;

const int N = 60;
int n, m;
int g[N][N];
int f[N * 2][N][N];

int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++ )
        for(int j = 1; j <= m; j ++ )
            scanf("%d", &g[i][j]);
            
    for(int k = 2; k <= n + m; k ++ )
        for(int i1 = max(1, k - m); i1 <= min(k - 1, n); i1 ++ )
            for(int i2 = max(1, k - m); i2 <= min(k - 1, n); i2 ++ ){
                int t = g[i1][k - i1];
                if (i1 != i2) t += g[i2][k - i2];
                for(int a = 0; a <= 1; a ++ )
                    for(int b = 0; b <= 1; b ++ )
                    {
                        f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1 - a][i2 - b] + t);
                    }
            }
    printf("%d\n", f[n + m][n][n]);
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值