线性dp(2)

题目1:最短编辑距离

解题思路:

集合f[i, j] :将 a[1 ~ i] 这个字符串 变成 b[1 ~ j] 的所有操作方法 的集合,存的是最少操作次数, 属性为min

分析f[i, j]的情况: 一定是从一下三种方法变成的, 我们只需求出这三种取Min

看第 a的 i 个字母

1. 删 a[i]

f[i, j] = f[i - 1, j] + 1;

2. 增 a[i + 1]

f[i, j] = f[i, j - 1] + 1;

3.

(1) a[i] == b[j] 不用改

(2) a[i] != b[j] 改

f[i, j] = f[i - 1, j - 1] + 0 / 1;

边界初始化:f[i][0] = i, f[0, i] = i;

实现代码:

#include <iostream>
using namespace std;

const int N = 1010;

int n, m;
int f[N][N];
char a[N], b[N];

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

题目2:摘花生

题目描述

Hello Kitty想摘点花生送给她喜欢的米老鼠。

她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。

地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。

Hello Kitty只能向东或向南走,不能向西或向北走。

问Hello Kitty最多能够摘到多少颗花生。

输入格式

第一行是一个整数T,代表一共有多少组数据。

接下来是T组数据。

每组数据的第一行是两个整数,分别代表花生苗的行数R和列数 C。

每组数据的接下来R行数据,从北向南依次描述每行花生苗的情况。每行数据有C个整数,按从西向东的顺序描述了该行每株花生苗上的花生数目M。

输出格式

对每组输入数据,输出一行,内容为Hello Kitty能摘到得最多的花生颗数。

解题思路:

构造集合f[i, j] : 从 (1, 1) 走 (i, j) 这个点的所有走法的集合,存的是所有走法中路径最长的 数,属性为max

f[i, j] = max(f[i - 1, j], f[i, j - 1]) + w[i, j]

边界初始化:f[1, 1] = w[1, 1]

实现代码:

#include <iostream>

using namespace std;


const int N = 110;

int f[N][N];
int w[N][N]; 

int main()
{
    int T;
    cin >> T;
    
    while (T --)
    {
        int R, C;
        cin >> R >> C;
        
        for (int i = 1; i <= R; i ++ )
            for (int j = 1; j <= C; j ++ )
                cin >> w[i][j];
                
        for (int i = 1; i <= R; i ++ )  // 行 
            for (int j = 1; j <= C; j ++ )  // 列 
                f[i][j] = max(f[i - 1][j], f[i][j - 1]) + w[i][j];
                
        cout << f[R][C] << endl;
    }    
    
    return 0;
 } 

题目3:方格取数

题目描述

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

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

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

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

输入格式

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

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

行和列编号从 1 开始。

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

输出格式

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

解题思路:

k = 2 (1, 1) k 代表横纵坐标之和

集合f[k , i1, i2] :表示从 (1, 1) 开始同时走, 走到 (i1, k - i1), (i2, k - i2) 的 所有走法的集合

存的是路径最大的数,属性为max。

分析f[k, i1, i2]的情况:

看一下两条路径分别从哪边走过来

(i1, j1) 上, 左

(i2, j2) 上, 左

t 表示走到 f[k, i1, i2] 所需加的数

i1 == i2, t = w[i1, j1]

i1 != i2, t = w[i1, j1] + w[i2, j2]

上, 上 f[k - 1, i1 - 1, i2 - 1]

上, 左 f[k - 1, i1 - 1, i2]

左, 上 f[k - 1, i1, i2 - 1]

左, 左 f[k - 1, i1, i2]

边界初始化:f[2, 1, 1] = w[1, 1]

实现代码:

#include <iostream>

using namespace std;

const int N = 15;

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

int main()
{
    int n;
    cin >> n;
    
    int a, b, c;
    while (cin >> a >> b >> c, a || b || c) w[a][b] = c;
    
    for (int k = 2; k <= n + n; k ++ )  // 横纵坐标之和 
        for (int i1 = 1; i1 <= n; i1 ++ )  // (i1, k - i1)
            for (int i2 = 1; i2 <= n; i2 ++ )  // (i2, k - i2)
            {
                int j1 = k - i1, j2 = k - i2;
                
                if (j1 < 1 || j1 > n || j2 < 1 || j2 > n) continue;
                int t = w[i1][j1];
                
                if (i1 != i2) t += w[i2][j2];
                int &x = f[k][i1][i2];
                
                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 + n][n][n] << endl;
    
    return 0;
    
}

题目4:宠物小精灵之收服

题目描述

宠物小精灵是一部讲述小智和他的搭档皮卡丘一起冒险的故事。

一天,小智和皮卡丘来到了小精灵狩猎场,里面有很多珍贵的野生宠物小精灵。

小智也想收服其中的一些小精灵。

然而,野生的小精灵并不那么容易被收服。

对于每一个野生小精灵而言,小智可能需要使用很多个精灵球才能收服它,而在收服过程中,野生小精灵也会对皮卡丘造成一定的伤害(从而减少皮卡丘的体力)。

当皮卡丘的体力小于等于0时,小智就必须结束狩猎(因为他需要给皮卡丘疗伤),而使得皮卡丘体力小于等于0的野生小精灵也不会被小智收服。

当小智的精灵球用完时,狩猎也宣告结束。

我们假设小智遇到野生小精灵时有两个选择:收服它,或者离开它。

如果小智选择了收服,那么一定会扔出能够收服该小精灵的精灵球,而皮卡丘也一定会受到相应的伤害;如果选择离开它,那么小智不会损失精灵球,皮卡丘也不会损失体力。

小智的目标有两个:主要目标是收服尽可能多的野生小精灵;如果可以收服的小精灵数量一样,小智希望皮卡丘受到的伤害越小(剩余体力越大),因为他们还要继续冒险。

现在已知小智的精灵球数量和皮卡丘的初始体力,已知每一个小精灵需要的用于收服的精灵球数目和它在被收服过程中会对皮卡丘造成的伤害数目。

请问,小智该如何选择收服哪些小精灵以达到他的目标呢?

输入格式

输入数据的第一行包含三个整数:N,M,K,分别代表小智的精灵球数量、皮卡丘初始的体力值、野生小精灵的数量。

之后的K行,每一行代表一个野生小精灵,包括两个整数:收服该小精灵需要的精灵球的数量,以及收服过程中对皮卡丘造成的伤害。

输出格式

输出为一行,包含两个整数:C,R,分别表示最多收服C个小精灵,以及收服C个小精灵时皮卡丘的剩余体力值最多为R。

解题思路:

集合f[i, j1, j2]: 表示从 前 i 件物品中选, 一维体积不超过 j1 (精灵球),二维体积不超过 j2 (体力)的所有选法的集合。w[i] = 1, 小精灵的价值看成 1,存的是所有方案里最大价值的数max。

分析f[i, j1, j2] 的情况:

第 i 件物品 选 或 不选

f[i, j1, j2] = max(f[i - 1, j1, j2], f[i - 1, j1 - v1i, j2 - v2i] + 1);

边界初始化:

f[0, j1, j2] = 0

实现代码:

#include <iostream>

using namespace std;

const int N = 1010, M = 510, K = 110;

int n, m, k;

int f[K][N][M];
int v1[K], v2[K];

int main()
{
    cin >> n >> m >> k;
    
    for (int i = 1; i <= k; i ++ ) cin >> v1[i] >> v2[i];
    
    for (int i = 1; i <= k; i ++)  // 物品数
        for (int j1 = 1; j1 <= n; j1 ++ )  // 一维体积(精灵球)
        {
            for (int j2 = 1; j2 <= m - 1; j2 ++ )
            {
                f[i][j1][j2] = f[i - 1][j1][j2];
                if (v1[i] <= j1 && v2[i] <= j2) 
                    f[i][j1][j2] = max(f[i][j1][j2], f[i - 1][j1 - v1[i]][j2 - v2[i]] + 1);
            }
                
         } 
        
    int res = -1, t;  // res 代表max(价值), t 消耗的二维体积(体力)
    for (int i = 0; i <= m - 1; i ++ )
        if (res < f[k][n][i])
        {
            res = f[k][n][i];
            t = i;
        }

     cout << res << ' ' << m - t << endl;
     
     return 0;
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值