动态规划dp

美丽序列

题目链接
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <string.h>
#include <iostream>
#define Mod 1000000007
using namespace std;
int main() {
    int n;
    long long a[42];
    long long dp[42][42][3][1602];
    // dp[i][j][1][k]代表当前 处理到第i个且值为j 在递减序列中第1个 前i个和为k
    memset(dp, 0, sizeof(dp));
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    //初始化
    if (a[1] == -1) {
        for (int i = 0; i <= 40; i++)
            dp[1][i][1][i] = 1;
    } else
        dp[1][a[1]][1][a[1]] = 1;
    for (int i = 2; i <= n; i++) {
        if (a[i] == -1) {  //若当前数为 -1 即可为任何数
            for (int j = 0; j <= 40; j++) {  //枚举当前可能的数 0~40
                for (int L = 0; L <= 40; L++) {  //枚举当前前一个(i-1)可能的数 0~40
                    for (int k = j * (i - 1); k <= 1600 - j; k++) {  //枚举前(i-1)个满足条件的和k
                        if (j >= L) {  //若当前大于前一个数 即打破递减序列的条件
                            dp[i][j][1][k + j] = (dp[i][j][1][k + j] + dp[i - 1][L][1][k]) % Mod;
                            dp[i][j][1][k + j] = (dp[i][j][1][k + j] + dp[i - 1][L][2][k]) % Mod;
                        } else
                            dp[i][j][2][k + j] = (dp[i][j][2][k + j] + dp[i - 1][L][1][k]) % Mod;



                    }
                }
            }
        } 
        else {                             //若为具体的大小
            for (int L = 0; L <= 40; L++) {  //枚举上一个数的大小
                for (int k = a[i] * (i - 1); k <= 1600 - a[i]; k++) {  //枚举前(i-1)个满足条件的和k
                    if (a[i] >= L) {
                        dp[i][a[i]][1][k + a[i]] = (dp[i][a[i]][1][k + a[i]] + dp[i - 1][L][1][k]) % Mod;
                        dp[i][a[i]][1][k + a[i]] = (dp[i][a[i]][1][k + a[i]] + dp[i - 1][L][2][k]) % Mod;
                    } else
                        dp[i][a[i]][2][k + a[i]] = (dp[i][a[i]][2][k + a[i]] + dp[i - 1][L][1][k]) % Mod;
                }
            }
        }
    }
    long long sum = 0;
    for (int j = 0; j <= 40; j++) {            //枚举 可能的大小
        for (int k = j * n; k <= 1600; k++) {  //枚举可能的和
            sum = (sum + dp[n][j][1][k]) % Mod;
            //当前数大小为j且在递减位置1 和为k的美丽序列数
            sum = (sum + dp[n][j][2][k]) % Mod;
        }
    }
    cout << sum << endl;
    return 0;
}

题解:就是dp,把有关的变量都适当列为dp数组的其中一维,然后反复推敲模拟。题解链接

1027. 方格取数

题目链接

#include <bits/stdc++.h>
using namespace std;
const int N = 15;

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

int main() {
    scanf("%d", &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++) {
            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);
                }
            }
        }
    }
    printf("%d\n", f[n + n][n][n]);

    return 0;
}

3996. 涂色

题目链接

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

using namespace std;

const int N = 5010;

int n;
int c[N], f[N][N];

int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &c[i]);
        if (i && c[i] == c[i - 1]) {
            i--;
            n--;
        }
    }

    for (int len = 2; len <= n; len++)
        for (int l = 0; l + len - 1 < n; l++) {
            int r = l + len - 1;
            if (c[l] != c[r])
                f[l][r] = min(f[l + 1][r], f[l][r - 1]) + 1;
            else
                f[l][r] = f[l + 1][r - 1] + 1;
        }

    printf("%d\n", f[0][n - 1]);
    return 0;
}

题解:区间dp

283. 多边形

题目链接

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = 110;
int n;
int p[M];
char c[M], ca;
int dp[M][M], dmin[M][M];
int w[M];
int main() {
    cin >> n;
    memset(dp, -0x3f, sizeof(dp));
    memset(dmin, 0x3f, sizeof(dmin));
    for (int i = 1; i <= n * 2; ++i) {
        if (i == 1) {
            getchar();
            scanf("%c", &c[n]);
        } else if (i & 1) {
            getchar();
            scanf("%c", &c[i / 2]);
            c[i / 2 + n] = c[i / 2];
        } else {
            scanf("%d", &p[i / 2]);
            dp[i / 2][i / 2] = dp[i / 2 + n][i / 2 + n] = p[i / 2 + n] =
                p[i / 2];
            dmin[i / 2][i / 2] = dmin[i / 2 + n][i / 2 + n] = p[i / 2];
        }
    }
    for (int len = 2; len <= n; ++len) {
        for (int i = 1; i + len - 1 <= n * 2; ++i) {
            int j = i + len - 1;
            for (int k = i; k < j; ++k) {
                if (c[k] == 't') {
                    dp[i][j] = max(dp[i][k] + dp[k + 1][j], dp[i][j]);
                    dmin[i][j] = min(dmin[i][k] + dmin[k + 1][j], dmin[i][j]);
                } else {
                    dp[i][j] = max(dp[i][k] * dp[k + 1][j], dp[i][j]);
                    dp[i][j] = max(dmin[i][k] * dmin[k + 1][j], dp[i][j]);

                    dmin[i][j] = min(dp[i][k] * dmin[k + 1][j], dmin[i][j]);
                    dmin[i][j] = min(dmin[i][k] * dmin[k + 1][j], dmin[i][j]);
                    dmin[i][j] = min(dmin[i][k] * dp[k + 1][j], dmin[i][j]);
                }
            }
        }
    }
    int maxv = -0x7f7f7f7f, len = 0;
    for (int i = 1; i <= n; ++i) {
        if (dp[i][i + n - 1] > maxv) {
            maxv = dp[i][i + n - 1];
            len = 0;
            w[len++] = i;
        } else if (dp[i][i + n - 1] == maxv) {
            w[len++] = i;
        }
    }
    cout << maxv << endl;
    for (int i = 0; i < len; ++i)
        cout << w[i] << " ";
    cout << endl;
    return 0;
}

题解:区间dp,要注意的点就是题目给的数字有负数,众所周知 因为负负得正,可能在某些时刻需要用最小值乘最小值来得到一个最大正值,所以我们求最大值的同时还得求一下最小值。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值