线性DP.

数字三角形朴素版

img

没有上面题目描述的“此外”!

代码:

#include<iostream>
#include<algorithm>
using namespace std;

const int N = 510; // 定义一个常数,表示数组的最大尺寸。

int n; // 声明一个变量来存储三角形的行数。
int w[N][N], f[N][N]; // 声明两个二维数组,用于存储三角形的值和计算结果。

int main()
{
	// 从输入中读取三角形的行数。
	cin >> n;

	// 循环读取三角形的值并存储在 'w' 数组中。
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= i; j++)
		{
			cin >> w[i][j];
		}
	}

	// 使用三角形的最后一行来初始化 'f' 数组。
	for (int i = 1; i <= n; i++)
	{
		f[n][i] = w[n][i];
	}

	// 动态规划循环,自底向上计算最大和。
	for (int i = n - 1; i > 0; i--)
	{
		for (int j = 0; j <= i; j++)
		{
			// 通过比较以下两个选项来计算每个位置 'f[i][j]' 的最大和:
			// 1. 将当前值 'w[i][j]' 添加到下方的值 'f[i+1][j]' 中。
			// 2. 将当前值 'w[i][j]' 添加到右下方的值 'f[i+1][j+1]' 中。
			f[i][j] = max(f[i + 1][j] + w[i][j], f[i + 1][j + 1] + w[i][j]);
		}
	}

	// 输出计算得到的最大和,即在 'f' 数组的顶部。
	cout << f[1][1] << endl;

	return 0;
}

数字三角形蓝桥杯21年题

原题链接:0数字三角形 - 蓝桥云课 (lanqiao.cn)

img

这道题和数字三角形很像,自然而然想到用DP来做,关键是如何保证向左下走和向右下走的次数相差不超过1.
这里我选择用三维DP来做:

状态表示 :f[i][j][k]表示(i,j)向左下走k步的路径最大和
状态计算 : f[i][j][k] = max(f[i+1][j][k-1],f[i+1][j+1][k]) ,其中第一项不一定存在,需要特判

那么我们最后设向左下走x,向右下走y,只要满足
x + y = n-1,|x - y| <= 1即可,从中取最大值即得解

代码1:

有些投机取巧,这样写的话一定是从最长的几条路径中选择满足题意的(左右绝对值差小于1),最长的几条路径中一定有满足题意的方案吗,可能会没有。(我没验证,只是提出疑问,但是过了)。

#include <iostream>

using namespace std;

const int N = 110;
const int INF = 1e9;

int a[N][N], f[N][N], d[N][N];

int main()
{
    int n;
    cin >> n;

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= i; j++)
            cin >> a[i][j];

    for (int i = 1; i < n; i++)
        for (int j = 0; j <= i + 1; j++)
            f[i][j] = -INF;

    f[1][1] = a[1][1];
    for (int i = 2; i <= n; i++)
        for (int j = 1; j <= i; j++)
            if (f[i - 1][j] < f[i - 1][j - 1])
            {
                f[i][j] = f[i - 1][j - 1] + a[i][j];
                d[i][j] = d[i - 1][j - 1] - 1;
            }
            else
            {
                f[i][j] = f[i - 1][j] + a[i][j];
                d[i][j] = d[i - 1][j] + 1;
            }

    int res = -INF;
    for (int i = 1; i <= n; i++)
        if (f[n][i] > res && abs(d[n][i]) <= 1)
            res = f[n][i];

    cout << res << endl;

    return 0;
}

代码2:

大佬写的 感觉 很nb。

出处:AcWing 3304. 数字三角形(蓝桥杯的题就这??) - AcWing

#include<cstdio>
using namespace std;
const int N = 104;
int f[N][N], n,s;

inline int max(int &a,int &b){return a < b ? b : a;}

int main(){
    scanf("%d",&n);
    for(int i = 1; i <= n; i++){
        for(int j = 1 ;j <= i; j++){
            scanf("%d",&f[i][j]);
            if(i>(n+2) >> 1) // 按 题目意思即可
                    if(j < (((n-1) >> 1)+i-n+1) || j > ((n >> 1)+1)) f[i][j]=-1;
        }
    }
    //状态转移方程: 找最大值即可
    for(int i = n - 1; i > 0; i--)
        for(int j = 1; j <= i;j ++)
            f[i][j] += max( f[i+1][j], f[i+1][j+1]); 
    printf("%d\n", f[1][1]);
    return 0;
}

最长公共子序列

例题:问题 - 1159 (hdu.edu.cn)

img

img

问题描述

给定序列的子序列是省略了一些元素(可能没有)的给定序列。给定一个序列 X = <x1, x2, …, xm>另一个序列 Z = <z1, z2, …, zk> 是 X 的子序列,如果存在一个严格递增的 X 索引序列 <i1, i2, …, ik>,使得对于所有 j = 1,2,…,k,xij = zj。例如,Z = <a, b, f, c> 是 X = <a, b, c, f, b, c> 的子序列,索引序列为 <1, 2, 4, 6>。给定两个序列 X 和 Y,问题是找到 X 和 Y 的最大长度公共子序列的长度。 程序输入来自文本文件。
文件中的每个数据集都包含两个表示给定序列的字符串。序列之间用任意数量的空格分隔。输入数据正确无误。对于每组数据,程序在标准输出上打印从单独行开头开始的最大长度公共子序列的长度。

示例输入

abcfbc abfcab
programming contest 
abcd mnp

示例输出

4
2
0

代码:

#include <iostream>
#include<cstring>
#include<algorithm>
#include<bits/stdc++.h>
using namespace std;

const int N = 1010;

//char a[N], b[N];
string a, b;

//int f[N][N];
int dp[N][N];
int LCS() {
    memset(dp, 0, sizeof dp);
    //核心代码:
    for (int i = 1; i <= a.length(); i++) {
        for (int j = 1; j <= b.length(); j++) {
            if (a[i - 1] == b[j - 1])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
    }
    /*
    for (int i = 0; i < a.length(); i++) {
        for (int j = 0; j < b.length(); j++) {
            if (a[i] == b[j])
                dp[i][j] = dp[i - 1][j - 1] + 1;//不能这样写,当i=0时下标为负
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
    }*/
    return dp[a.length()][b.length()];
    //return dp[a.length() - 1][b.length() - 1];

}
int main()
{
    
    while (cin >> a >> b) {
        //cout << a[] << endl;
        //cout << a.length() << " " << b.length() << endl;
        cout << LCS() << endl;
    }
    /*
    int n, m;
    cin>>n>>m;
    cin >> a + 1 >> b + 1;
    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]);
            if (a[i] == b[j])
                f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
        }
    }
    cout << f[n][m] << endl;
    */
    return 0;
}

最短编辑距离

题目描述
给定两个字符串A和B,现在要将A经过若干操作变为B,可进行的操作有:

1.删除–将字符串A中的某个字符删除。
2.插入–在字符串A的某个位置插入某个字符。
3.替换–将字符串A中的某个字符替换为另一个字符。

现在请你求出,将A变为B至少需要进行多少次操作。
输入格式
第一行包含整数n,表示字符串A的长度。
第二行包含一个长度为n的字符串A。
第三行包含整数m,表示字符串B的长度。
第四行包含一个长度为m的字符串B。
字符串中均只包含大写字母。
输出格式
输出一个整数,表示最少操作次数。
数据范围
1≤n,m≤1000
输入样例:

10
AGTCTGACGC
11
AGTAAGTAGGC

输出样例:

4

img

对于该题应该以整体方式进行考虑,首先考虑末尾
当最后一位不匹配时以下如图三种情况。
AB两线段表示AB两数组

情况图

#include <iostream>
#include<cstring>
#include<algorithm>

using namespace std;

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



int main()
{
    ios::sync_with_stdio(false);
    cin.tie(); cout.tie();
    cin >> n >> a + 1;
    cin >> m >> b + 1;
    for (int i = 0; i <= m; i++) f[0][i] = i;
    for (int i = 0; i <= n; i++) f[i][0] = 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);//情况1,2
            if (a[i] == b[j])//情况3的两种情况
                f[i][j] = min(f[i][j], f[i - 1][j - 1]);
            else
                f[i][j] = min(f[i][j], f[i - 1][j - 1] + 1);
        }
    }
    cout << f[n][m] << endl;
    return 0;
}
  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值