Week13-动态规划(三)

A : 石子合并

题目描述
将 堆石子绕圆形操场排放,现要将石子有序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。

请编写一个程序,读入堆数N及每堆的石子数,并进行如下计算:

选择一种合并石子的方案,使得做 次合并得分总和最大。
选择一种合并石子的方案,使得做 次合并得分总和最小。
输入描述
输入第一行一个整数N(1≤N≤100)(1≤N≤100),表示有N堆石子。

第二行N个整数,表示每堆石子的数量。

输出描述
输出共两行:

第一行为合并得分总和最小值,

第二行为合并得分总和最大值。

Case 1
Input
4
4 5 9 4
Output
43
54

#include <iostream>
#include <cstring>
// #include <queue>

// 14 18 22 54
// 8 13 22 43

typedef long long un;

un fmax(int i, int j, un **f, bool **mk, un *ns)
{
    if (i == j) return 0;
    if (!mk[i][j]) {
        un maxs = 0;
        for (int k = i; k < j; k++)
        {
            maxs = std::max(maxs, fmax(i, k, f, mk, ns) + fmax(k + 1, j, f, mk, ns));
        }
        
        // f[i][j] = maxs + ([&](int l, int r){ un a = 0; for (;l <= r; l++) { a += ns[l]; } return a; })(i, j);
        mk[i][j] = true;
        // std::cout << maxs << "|" << i << "-" << j << ", ";
        for (int x = i; x <= j; x++)
        {
            maxs += ns[x];
        }
        f[i][j] = maxs;
    }
    return f[i][j];
}

un fmin(int i, int j, un **f, bool **mk, un *ns)
{
    if (i == j) return 0;
    if (!mk[i][j]) {
        un mins = i == j ? 0 : 9999999;
        for (int k = i; k < j; k++)
        {
            mins = std::min(mins, fmin(i, k, f, mk, ns) + fmin(k + 1, j, f, mk, ns));
        }
        f[i][j] = mins + ([&](int l, int r){ un a = 0; for (;l <= r; l++) { a += ns[l]; } return a; })(i, j);
        mk[i][j] = true;
    }
    return f[i][j];
}


un del(bool max, un *lst, int n, int offset)
{
    int xi = 0;
    un **f = new un*[200];
    bool **mk = new bool*[200];
    un *ns = new un[200];
    for (int i = 0; i < 200; i++)
    {
        f[i] = new un[200];
        mk[i] = new bool[200];
        memset(mk[i], 0, sizeof(bool) * 200);
    }
    memset(ns, 0, sizeof(un) * 200);
    
    while (xi < n)
    {
        ns[xi] = max ? lst[xi + offset] : lst[xi + offset];
        xi++;
    }
    un res = (max ? fmax(0, n - 1, f, mk, ns) : fmin(0, n - 1, f, mk, ns));
    delete[] f;
    delete[] mk;
    delete[] ns;
    return res;
}

int main()
{
    un *lt;
    un n, i = 0;
    std::cin >> n;
    lt = new un[n * 2];
    while (i < n)
    {
        std::cin >> lt[i];
        lt[i + n] = lt[i];
        i++;
    }
    un mx = del(true, lt, n, 0);
    un mn = del(false, lt, n, 0);
    
    for (int i = 1; i < n; i++)
    {
        mx = std::max(mx, del(true, lt, n, i));
        mn = std::min(mn, del(false, lt, n, i));
    }
    std::cout << mn << std::endl << mx << std::endl;
    return  0;
}

在这里插入图片描述

B : 括号序列

题目描述
定义一个合法的括号序列

空序列合法

如果S合法,那么(S)和[S]合法

假设A和B合法,那么AB合法

合法序列 - (),[],(()),([]),()[],()[()]

非法序列 - (,[,],)(, (]),([(),([)]

给定一个序列,求最少添加多少个括号,才能得到一个合法序列。

输入描述
输入仅一行,为一个字符串S

输出描述
输出只有一个整数,表示增加的最少字符数。

Case 1
Input
[])
Output
1

#include <iostream>
#include <iostream>

int f(int i, int j, std::string& s, bool **mk, int **fr)
{
    int mn = 999999;
    if (mk[i][j]) return fr[i][j]; 
    if (i == j) return 1;
    if (i > j) return 0;
    if (i + 1 == j)
    {
        if (s[i] == '(' && s[j] == ')') return 0;
        if (s[i] == '[' && s[j] == ']') return 0;
        if (s[i] == '{' && s[j] == '}') return 0;
        return 2;
    }
    for (int k = i; k < j; k++)
    {
        mn = std::min(f(i, k, s, mk, fr) + f(k + 1, j, s, mk, fr), mn);
    }
    if (s[i] == '(' && s[j] == ')') mn = std::min(f(i + 1, j - 1, s, mk, fr), mn);
    if (s[i] == '[' && s[j] == ']') mn = std::min(f(i + 1, j - 1, s, mk, fr), mn);
    if (s[i] == '{' && s[j] == '}') mn = std::min(f(i + 1, j - 1, s, mk, fr), mn);
    if (s[i] == '(' && s[j] != ')') mn = std::min(f(i + 1, j, s, mk, fr) + 1, mn);
    if (s[i] == '[' && s[j] != ']') mn = std::min(f(i + 1, j, s, mk, fr) + 1, mn);
    if (s[i] == '{' && s[j] != '}') mn = std::min(f(i + 1, j, s, mk, fr) + 1, mn);
    if (s[i] != '(' && s[j] == ')') mn = std::min(f(i, j - 1, s, mk, fr) + 1, mn);
    if (s[i] != '[' && s[j] == ']') mn = std::min(f(i, j - 1, s, mk, fr) + 1, mn);
    if (s[i] != '{' && s[j] == '}') mn = std::min(f(i, j - 1, s, mk, fr) + 1, mn);
    fr[i][j] = mn;
    mk[i][j] = true;
    return mn;
}



int main()
{
    bool **mk;
    int **fr;
    std::string s;
    std::cin >> s;
    mk = new bool*[s.size()];
    fr = new int*[s.size()];
    for (int i = 0; i < s.size(); i++)
    {
        mk[i] = new bool[s.size()];
        fr[i] = new int[s.size()];
        for (int j = 0; j < s.size(); j++)
        {
            mk[i][j] = false;
        }
    }
    std::cout << f(0, s.size() - 1, s, mk, fr) << std::endl;
    return  0;
}

在这里插入图片描述

C : 最长回文子序列

题目描述
给定一个字符串S ,找到其中最长的回文子序列,并返回该序列的长度。

输入描述
输入为一个只包含小写英文字母的字符串S,最大长度不超过3000。

输出描述
输出最长回文子序列的长度。

Case 1
Input
abcb
Output
3

#include <iostream>

std::string str;
bool mk[4000][4000];
int fr[4000][4000];

int f(int i, int j)
{
    if (i == j) return 1;
    if (i > j) return 0;
    if (mk[i][j]) return fr[i][j];
    if (str[i] == str[j])
    {
        mk[i][j] = true;
        fr[i][j] = f(i + 1, j - 1) + 2;
        return fr[i][j];
    }
    fr[i][j] = std::max(f(i + 1, j), f(i, j - 1));
    mk[i][j] = true;
    return fr[i][j];
}

int main()
{
    std::cin >> str;
    std::cout << f(0, str.size() - 1);
    return 0;
}

在这里插入图片描述

D : 方格填充

题目描述
将高为H,宽为W的棋盘分割成若干个宽和高分别为1和2的长方形,有多少种方案。

输入描述
第一行为H(1≤N≤11)(1≤N≤11),W(1≤W≤11)(1≤W≤11)。

输出描述
输出方案数。

Case 1
Input
2 2
Output
2

#include <iostream>
#include <cstring>
using namespace std;
typedef long long un;
const un maxN = 12;
un f[maxN][1 << 12];
un sulv[9999999][2];
un res;
int H, W;
int main()
{
    res = 0; 
    cin >> H >> W;
    for(int i = 0; i < (1 << W); i++)
    {
        for(int j = 0; j < (1 << W); j++)
        {
            bool ok = true;
            for(int k = 0; k < W; k++)
            {
                if(ok)
                {
                    if((i & (1 << k)) == 0)
                    {
                        if((j & (1 << k)) == 0)
                        {
                            ok = false;
                            break;
                        }
                    }
                    else
                    {
                        if((j & (1 << k)) == 0)
                            continue;
                        k++;
                        if(k >= W || ((i & (1 << k)) == 0))
                        {
                            ok = false;
                            break;
                        }
                        else
                        {
                            if((j & (1 << (k - 1))) && (j & (1 << (k))) == 0)
                            {
                                ok = false;
                                break;
                            }
                            else if((j & (1 << (k - 1))) == 0 && (j & (1 << (k))))
                                k--;
                        }
                    } 
                }
            }
            if(ok)
            {
                sulv[res][0] = i;
                sulv[res++][1] = j;
            }
        }
    }
    memset(f, 0, sizeof(f));
    f[0][(1 << W) - 1] = 1;
    for(int i = 0; i < H; i++)
    {
        for(int j = 0; j < res; j++)
        {
            f[i + 1][sulv[j][1]] += f[i][sulv[j][0]];
        }
    }
    cout << f[H][(1 << W) - 1];
    return 0;
}

在这里插入图片描述

思路

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最后一题:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
课上基本讲的差不多了,自己做的话还是挺有难度的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值