首页 > 其他分享 >动态规划5.2-区间动态规划

动态规划5.2-区间动态规划

时间:2023-07-29 23:44:26浏览次数:32  
标签:5.2 int 石子 合并 合法 括号 序列 动态 规划

一、区间动态规划

区间动态规划是动态规划中的一类题,下面先引入几个题目,最后总结一下此类问题的相关解题思路

二、例题

1.[Daimayuan Online Judge.石子合并]

题目描述

有 \(n\) 堆石子排成一排,第 \(i\) 堆石子有 \(a_i\) 颗,每次我们可以选择相邻的两堆石子合并,代价是两堆石子数目的和,现在我们要一直合并这些石子,使得最后只剩下一堆石子,问总代价最少是多少?

输入格式

第一行一个数字 \(n\)。
接下来一行 \(n\) 个整数 \(a_1,a_2,…,a_n\)。

输出格式

一行一个整数,表示答案。

输入样例
3
1 2 3
输出样例
9
数据范围

对于所有数据,保证 \(1≤n,a_i≤500\)。

解题思路

对于第 \(i\) 和 \(i+1\) 堆石子合并,代价为 \(a_i+a_{i+1}\),\(1≤i<n\)。因此需要预处理前缀和

  • 状态表示:\(f[i][j]\) 表示合并 \(i\) 到 \(j\) 堆石子的代价,需要求解最小值
  • 状态计算:合并第 \(i\) 到 \(j\) 堆石子,可分为合并 \(f[i][k]\) 和 \(f[k+1][j]\),\(k\in[i,j)\),故 \(f[i][j]=f[i][k]+f[k+1][j]+a_i+a_{i+1}+...+a_{j}\),枚举分界点 \(k\) 求解最小值
C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 510;

int n;
int a[N], s[N];
int f[N][N];

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        s[i] = s[i - 1] + a[i];
    }
    for (int len = 2; len <= n; len++) {
        for (int i = 1; i + len - 1 <= n; i++) {
            int l = i, r = i + len - 1;
            f[l][r] = 0x3f3f3f3f;
            for (int k = l; k < r; k++)
                f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
        }
    }
    printf("%d\n", f[1][n]);
    return 0;
}

2.[Daimayuan Online Judge.括号序列]

题目描述

给定一个长度为 \(n\) 的字符串 \(s\),字符串由 \((, ), [, ]\) 组成,问其中最长的合法子序列有多长?也就是说,我们要找到最大的 \(m\),使得存在 \(i_1,i_2,…,i_m\) 满足 \(1≤i_1<i_2<⋯<i_m≤n\) 并且 \(s_{i_1}s_{i_2}…s_{i_m}\) 是一个合法的括号序列。
合法的括号序列的定义是:

  • 空串是一个合法的括号序列。
  • 若 \(A\) 是一个合法的括号序列,则 \((A), [A]\) 也是合法的括号序列。
  • 若 \(A, B\) 都是合法的括号序列,则 \(AB\) 也是合法的括号序列。
输入格式

第一行一个数字 \(n\)。
接下来一行,一个长度为 \(n\) 的字符串 \(s\)。

输出格式

一行一个整数,表示答案。

输入样例
10
]]][()]])[
输出样例
4
数据范围

对于所有数据,保证 \(1≤n≤500\)。

解题思路
  • 状态表示:\(f[i][j]\) 表示 \(i\) 到 \(j\) 的合法括号序列,属性值为长度
  • 状态计算:根据合法括号序列的定义,对于 \(f[i][j]\),如果 \(s_i=s_j\),则 \(f[i][j]=f[i++1][j-1]+2\),否则 \(f[i][j]=max(f[i][j],max(f[i+1][j],f[i][j+1]))\);另外,根据 “若 \(A, B\) 都是合法的括号序列,则 \(AB\) 也是合法的括号序列”,可以枚举分界点,进行括号序列的拼接,即 \(f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]),k\in[i,j)\),且这种情况可以包含 \(s_i\neq s_j\) 这种情况,减少代码量
C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 510;

int n;
char s[N];
int f[N][N];

int main() {
    scanf("%d%s", &n, s + 1);
    for (int len = 2; len <= n; len++) {
        for (int i = 1; i + len - 1 <= n; i++) {
            int l = i, r = i + len - 1;
            if (s[l] == '[' && s[r] == ']' || s[l] == '(' && s[r] == ')')
                f[l][r] = f[l + 1][r - 1] + 2;
            for (int k = l; k < r; k++)
                f[l][r] = max(f[l][r], f[l][k] + f[k + 1][r]);
        }
    }
    printf("%d\n", f[1][n]);
    return 0;
}

3.[Daimayuan Online Judge.石子合并2]

题目描述

有 \(n\) 堆石子围成一个圈,第 \(i\) 堆石子有 \(a_i\) 颗,每次我们可以选择相邻的两堆石子合并,代价是两堆石子数目的和,现在我们要一直合并这些石子,使得最后只剩下一堆石子,问总代价最少是多少?

输入格式

第一行一个数字 \(n\)。
接下来一行 \(n\) 个整数 \(a_1,a_2,…,a_n\)。

输出格式

一行一个整数,表示答案。

输入样例
3
1 3 2
输出样例
9
数据范围

对于所有数据,保证 \(1≤n≤250,1≤a_i≤500\)。

解题思路

典型的环型区间动态规划问题,此类题目可以将序列重复一遍,也就是令 \(a_i=a_{i+n}\),这样当作一个链式区间动态规划问题进行求解,这样的话先求解整个的区间结果,最后取其中的一部分即可,结合本题目就是,先求解 \(a_1\) 到 \(a_{2n}\) 的最小代价,然后取其中长度为 \(n\) 的合并代价取最小值

C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 510;

int n;
int a[N], s[N];
int f[N][N];

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        a[i + n] = a[i];
    }
    for (int i = 1; i <= 2 * n; i++)
        s[i] = s[i - 1] + a[i];
    for (int len = 2; len <= n; len++)
        for (int i = 1; i + len - 1 <= 2 * n; i++) {
            int l = i, r = i + len - 1;
            f[l][r] = 0x3f3f3f3f;
            for (int k = i; k < r; k++)
                f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
        }
    int ans = 0x3f3f3f3f;
    for (int i = 1; i <= n; i++)
        ans = min(ans, f[i][i + n - 1]);
    printf("%d\n", ans);
    return 0;
}

三、总结

区间动态规划的问题的求解还是比较有套路的,基本状态表示都是二维表示区间的左右端点,当然某些问题可能涉及到最后在哪个端点处,这样就需要额外考虑一维,用 \(0\) 和 \(1\) 两种状态来区分。

标签:5.2,int,石子,合并,合法,括号,序列,动态,规划
From: https://www.cnblogs.com/Cocoicobird/p/17581300.html

相关文章

  • 我用WebGL打造了一款动态壁纸
    我用WebGL打造了一款动态壁纸简述最近在给自己电脑换壁纸的时候发现了一张很有特点的图(就是下面这张),于是我突发奇想,要是能把这张图变成一张动态的壁纸。那该多好。于是我打算用threejs开发一个3D的动态壁纸网页。相关技术Vite+Vue、Threejs、TwinSpace(我自己基于Threejs封装的一个......
  • 我用WebGL打造了一款动态壁纸
    我用WebGL打造了一款动态壁纸简述最近在给自己电脑换壁纸的时候发现了一张很有特点的图(就是下面这张),于是我突发奇想,要是能把这张图变成一张动态的壁纸。那该多好。于是我打算用threejs开发一个3D的动态壁纸网页。相关技术Vite+Vue、Threejs、TwinSpace(我自己基于Threejs封装......
  • 不同层级的Android开发者的不同行为,我们该如何进阶和规划?
    四个层级如下:第一层:普通程序员第二层:熟练开发者、高级开发工程师、技术组长第三层:技术专家、架构师、一线经理第四层:科学家、研究员、首席(资深)架构师、部门研发总监imageAndroid开发者的四个层级按我的理解,无论是Android开发者还是其他的开发者都可以分为四个层级,可依次对应普......
  • 微信小程序 button 等组件单击动态传递参数
    首先在小程序端,加入一个data-*的参数,‘*’需要是小写,若写成大写会被转换为小写,支持‘-’号,如<buttonbindtap="button-click"data-abc="{{value}}"></button>然后在js文件中实现‘click’方法,通过event.currentTarget.dataset.*获取在小程序端写的值button-click:function(e......
  • 通过js动态改变样式、改变伪类样式
    1、设置变量2、使用变量3、动态设置变量......
  • 动态逆序对
    [CQOI2011]动态逆序对考虑CDQ分治。可以对于每个数记录一个时间戳,表示它被删除的时间(为了树状数组的维护方便,我们记时间戳越大者删除时间越早)。然后逆序对的下标是一维,数值是一维,转换成了一个三维偏序问题。我们对于每个数,考虑由它构成的逆序对,且它的时间戳为二者中较大者构......
  • ajax动态加载JS不执行的解决办法
    //第一步:匹配加载的页面中是否含有jsvarregDetectJs=/<script(.|\n)*?>(.|\n|\r\n)*?<\/script>/ig;varjsContained=ajaxLoadedData.match(regDetectJs);//第二步:如果包含js,则一段一段的取出js再加载执行if(jsContained){ //分段取出js正则 varregGetJS=/<sc......
  • 动态构建IN查询数据格式的Oracle SQL实现方法
    背景在实际的数据库查询中,经常会遇到根据不同条件动态构建IN查询的需求。例如,当选择一个部门时,需要查询指定部门的数据;当选择多个部门时,需要查询多个部门的数据。在OracleSQL中,我们不能直接在一条SQL查询中动态构建IN查询的数据格式。然而,使用CASEWHEN语句,我们可以巧妙地解决这个......
  • SpringBoot学习---第五篇:动态数据源(多数据源自动切换)
    目录一、应用场景二、准备工作2.1 创建数据表2.2添加依赖2.3生成bean、dao、mapper三、动态数据源3.1 配置文件application.properties3.2动态数据源核心代码3.3 启动类添加注解四、使用方法4.1Controller4.2Service五、测试六、Springboot2.0动态多数据源切换一、应用......
  • Django 之前端动态数据展示
    一、结合前端页面实现ORM对数据的增删改查1、修改和删除功能的逻辑'''修改功能的逻辑'''1、确定修改哪条记录,怎么确定?通过主键id确定唯一一条记录2、点击修改的按钮,应该跳转到一个修改的页面3、应该通过id查询到原来的数据,并且把这个记录的数据展示到修改的页面4、开始......