曾经沧海难为水,除却巫山不是云。|

Joey-Wang

园龄:4年3个月粉丝:17关注:0

11.1~11.6 动态规划

11.1~11.6 动态规划

11.2 最大连续子序列

http://codeup.hustoj.com/contest.php?cid=100000626

image-20200831224406405

题目解析

因为要输出序列的首尾元素,所以定义了结构体

代码

#include <cstdio>
#include <cstring>

#define maxn 10005
struct node {
    int s, t; //对应a中的起点终点值
    int sum;
} dp[maxn];// dp[i]表示以i结尾的最大连续子序列和
int main() {
    int n;
    int a[maxn];
    while (scanf("%d", &n) && n) {
        memset(dp, 0, sizeof(dp));
        for (int i = 0; i < n; i++) {
            scanf("%d", &a[i]);
        }
        dp[0].s = dp[0].t = dp[0].sum = a[0];
        for (int i = 1; i < n; i++) {
            if (a[i] >= dp[i - 1].sum + a[i]) {
                dp[i].s = dp[i].t = dp[i].sum = a[i];
            } else {
                dp[i].s = dp[i - 1].s;
                dp[i].t = a[i];
                dp[i].sum = dp[i - 1].sum + a[i];
            }
        }
        int index = 0;
        for (int i = 1; i < n; i++) {
            if (dp[i].sum > dp[index].sum) {
                index = i;
            }
        }
        if (dp[index].sum < 0) printf("0 %d %d\n", a[0], a[n - 1]);
        else printf("%d %d %d\n", dp[index].sum, dp[index].s, dp[index].t);
    }
    return 0;
}

11.3 最长上升(不下降)子序列 LIS

http://codeup.hustoj.com/contest.php?cid=100000627

image-20200831230441357

题目解析

emmmmm没事哈好说的,纯粹模板题

代码

#include <cstdio>
#include <algorithm>

using namespace std;
#define maxn 1005

int main() {
    int n;
    int a[maxn], dp[maxn]; //dp[i]表示以a[i]结尾的最长上升子序列的长度
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    int ans = -1;
    for (int i = 0; i < n; i++) {
        dp[i] = 1; //初始化
        for (int j = 0; j < i; j++) {
            if (a[j] <= a[i]) {
                dp[i] = max(dp[i], 1 + dp[j]);
            }
        }
        if (dp[i] > ans) ans = dp[i];
    }
    printf("%d\n", ans);
    return 0;
}

11.4 最长公共子序列

http://codeup.hustoj.com/contest.php?cid=100000628

image-20200831232015842

题目解析

递归边界若是dp[i][0]=dp[0][j]=0,则字符串下标1开始。
此处用string记录字符串,故字符串下标从0开始,故用
dp[i + 1][j + 1]=dp[i][j] + 1(若a[i] = b[j])
dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]);(若a[i] != b[j])

代码

#include <cstdio>
#include <iostream>
#include <string>
#include <algorithm>

#define maxn 105
using namespace std;

int main() {
    string a, b;
    int dp[maxn][maxn]; //dp[i+1][j+1]表示a[i]和b[j]之前的最长公共子序列长度
    while (cin >> a >> b) {
        for (int i = 0; i <= a.length(); i++) {
            dp[i][0] = 0;
        }
        for (int i = 0; i <= b.length(); i++) {
            dp[0][i] = 0;
        }
        for (int i = 0; i < a.length(); i++) {
            for (int j = 0; j < b.length(); j++) {
                if (a[i] == b[j]) dp[i + 1][j + 1] = dp[i][j] + 1;
                else dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]);
            }
        }
        printf("%d\n",dp[a.length()][b.length()]);
    }
    return 0;
}

11.5 最长回文字符串

http://codeup.hustoj.com/contest.php?cid=100000629

image-20200901002548365

题目解析

将输入的字符串读入buf,将buf中的字母及数字存储到要进行回文比较的字符串s中(字母都转化成小写),为了最后能输对应的buf,用 p 数组记录 s 下标到 buf 下标的映射。

回文比较采用枚举回文串“中间”的i,然后不断向外扩张(书中的方法),此处要记录最长回文串长度以及起点在字符串s中的下标(都记录第一个>max_len的值,因为题目中要求若有多个相同长度回文串,输出起始位置最靠左的,也就是 i 最小的)

代码

#include <cstdio>
#include <cstring>
#include <algorithm>

#define maxn 5005
char buf[maxn], s[maxn];
int p[maxn]; //s下标到buf的映射
bool dp[maxn][maxn] = {false}; //dp[i][j]——s数组中下标i到j的字符串是否为回文字符串
int main() {
    gets(buf);
    int s_len = 0;
    // 将buf中的字母变小写和数字一起放入s,建立s下标到buf的映射p
    for (int i = 0; i < strlen(buf); i++) {
        if (isalpha(buf[i])) {
            s[s_len] = tolower(buf[i]);
            p[s_len] = i;
            s_len++;
        } else if (isdigit(buf[i])) {
            s[s_len] = buf[i];
            p[s_len] = i;
            s_len++;
        }
    }
    int max_len = 1, start = 0;
    for (int i = 0; i < s_len; i++) {
        dp[i][i] = true;
        if (i < s_len - 1 && s[i] == s[i + 1]) {
            dp[i][i + 1] = true;
            if (max_len < 2) {
                max_len = 2;
                start = i;
            }
        }
    }
    for (int L = 3; L <= s_len; L++) {
        for (int i = 0; i + L - 1 < s_len; i++) {
            int j = i + L - 1;
            if (s[i] == s[j] && dp[i + 1][j - 1]) {
                dp[i][j] = true;
                if (max_len < L) {
                    max_len = L;
                    start = i;
                }
            }
        }
    }
    for (int i = p[start]; i <= p[start + max_len - 1]; i++) {
        printf("%c", buf[i]);
    }
    printf("\n");
    return 0;
}

10.7 关键路径

http://codeup.hustoj.com/contest.php?cid=100000627

image-20200901020549631

题目解析

这道题用了dp做,书p439页有讲解
PS:个人觉得这里输入给出的各顶点abcde没啥用,但有可能不是按照26个英文字母顺序给出的顶点
(我默认顶点是顺序给出的AC了)

用拓扑排序的代码可参考:https://blog.csdn.net/morizunzhu/article/details/96652800

代码

#include <cstdio>
#include <algorithm>
#include <iostream>

using namespace std;
#define maxn 20
#define INF 0x3fffffff
int n, G[maxn][maxn]; //存图
int dp[maxn]; //dp[i]表示以i为起点的能得到的最长路径长度
int choose[maxn];

int DP(int i) {
    if (dp[i] > 0) return dp[i];
    for (int j = 0; j < n; j++) {
        if (G[i][j] != INF) {
            int temp = DP(j) + G[i][j];
            if (temp > dp[i]) {
                dp[i] = temp;
                choose[i] = j;
            }
        }
    }
    return dp[i];
}

void printPath(int i) {
    while (choose[i] != -1) {
        printf("(%c,%c) ", i + 97, choose[i] + 97);
        i = choose[i];
    }
}

int main() {
    int t, m, w;
    char a, b;
    scanf("%d", &t);
    while (t--) {
        fill(G[0], G[0] + maxn * maxn, INF);
        fill(dp, dp + maxn, 0);
        fill(choose, choose + maxn, -1);
        scanf("%d%d", &n, &m);
        char temp;
        for (int i = 0; i < n; i++) {
            cin >> temp;
        }
        for (int i = 0; i < m; i++) {
            cin >> a >> b >> w;
            G[a - 97][b - 97] = w;
        }
        int ans = 0, k,tt;
        for (int i = 0; i < n; i++) {
            tt = DP(i);
            if (tt > ans) {
                ans = tt;
                k=i;
            }
        }
        printPath(k);
        printf("%d\n",ans);
    }
    return 0;
}

11.6 DAG 最长路 🌟

http://codeup.hustoj.com/contest.php?cid=100000630

image-20200901023502064

题目解析

将此问题是典型 DAG 最长路问题,需将每个矩形都看作一个顶点,并将嵌套关系视为顶点之间的有向边,边权为1。

1️⃣ 构造DAG图,便于使用DP求DAG的最长路 ——将每个矩形变为一个顶点,根据矩形间是否能嵌套,判断两个顶点间是否有有向边

  • 这里定义了结构体rect存储输入的矩形的宽,因为嵌套条件是a<c,b<d或者b<c,a<d,所以为了之后比较方便,统一将输入的更大的数作为长,另一个数作为宽
  • 所有矩形数据输入完毕后,开始两两比较能否嵌套,默认每个矩形代表的顶点编号根据输入的顺序依次0~n-1,若能嵌套(r[i].l < r[j].l && r[i].w < r[j].w),则令顶点 i 与 j 之间有一条有向边( G[i][j] = 1)

2️⃣ 构造完 DAG图后,就是常规操作,使用DP计算最长路长度ans

3️⃣ 需注意的是,最后要输出的是能最多嵌套的矩形数目,⚠️ 所以输出ans+1

代码

#include <cstdio>
#include <algorithm>

#define maxn 1005
#define INF 0x3fffffff
using namespace std;
struct rect {
    int l, w;
} r[maxn];
int n, G[maxn][maxn];
int dp[maxn];

int DP(int i) {
    if (dp[i] > 0) return dp[i];
    for (int j = 0; j < n; j++) {
        if (G[i][j] != INF) {
            dp[i] = max(dp[i], DP(j) + G[i][j]);
        }
    }
    return dp[i];
}

int main() {
    int t, a, b;
    scanf("%d", &t);
    while (t--) {
        fill(G[0], G[0] + maxn * maxn, INF);
        fill(dp, dp + maxn, 0);
        scanf("%d", &n);
        for (int i = 0; i < n; i++) {
            scanf("%d%d", &a, &b);
            if (a >= b) r[i].l = a, r[i].w = b;
            else r[i].l = b, r[i].w = a;
        }
        for (int i = 0; i < n - 1; i++) {
            for (int j = i + 1; j < n; j++) {
                if (r[i].l < r[j].l && r[i].w < r[j].w) {
                    G[i][j] = 1;
                }
            }
        }
        int ans = 0, temp;
        for (int i = 0; i < n; i++) {
            temp = DP(i);
            if (temp > ans) ans = temp;
        }
        printf("%d\n",ans+1);
    }
    return 0;
}

本文作者:Joey-Wang

本文链接:https://www.cnblogs.com/joey-wang/p/14541195.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Joey-Wang  阅读(71)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开