区间dp与环形dp

区间dp

常见题型

求区间[1,n]XXXXX后的最大/小值,一般分为无要求或只能/最多分成m段两类

做法

如对分段无要求,设dp[i][j]表示序列中[i,j]的最值,最外层循环区间长度,第二层循环左端点,并能确定右端点,第三层枚举断点;

for(rint len = 1;len <= n; ++len) {//如果对len == 1初始化了可从2枚举
    for(rint i = 1,j = i + len - 1;j <= n;++i,++j) {
        for(rint k = i;k < j; ++k) {//把序列分成[i,k],[k+1,j];
            .......;
        }
    }
}

环形dp

常见题型

求首尾相接的区间[1,n]XXXXX后的最大/小值;

做法

可拆链转化为区间dp求解

转化方法

对输入的原区间复制一遍接到序列后,则该2n区间任意长为n的区间为原环断开的一种链。

合并石子

非常基础的模板题

#include <iostream>
#include <cstdio>
#include <queue>
#include <stack>
#include <cmath>
#include <map>
#include <cstring>
#include <algorithm>
#define ll long long
#define rint register int
#define lson (x << 1)
#define rson (x << 1 | 1)
#define mid ((st[x].l + st[x].r) >> 1)
using namespace std;
template <typename xxx>
inline void read(xxx &x) {
    x = 0;
    int f = 1;
    char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-')
            f = -1;
        c = getchar();
    }
    while (c <= '9' && c >= '0') {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    x *= f;
}
template <typename xxx>
inline void print(xxx x) {
    if (x < 0) {
        x = -x;
        putchar('-');
    }
    if (x > 9)
        print(x / 10);
    putchar(x % 10 + '0');
}
const int N = 300100;
const int mod = 100003;
const int maxn = 100100;
const int inf = 0x7fffffff;
const int key = 131;
const double eps = 1e-9;
int n;
ll a[444];
ll sum[444];
ll dp[444][444];
ll f[444][444];
int main() {
    ll ans=0,fk=1e15;
    read(n);
    for(rint i=1;i<=n;++i) read(a[i]),a[i+n]=a[i];
    for(rint i=1;i<=(n<<1);++i)
    {
        sum[i]=sum[i-1]+a[i];
        for(rint j=i;j<=(n<<1);++j)
            if(j^i) f[i][j]=1e15;
    }
    for(rint len=1;len<n;++len)
    {
        for(rint i=1;i<=(n<<1);++i)
        {
            rint j=i+len;
            for(rint k=i;k<j;++k)
            {
                dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
                f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
            }
        }
    }
    for(rint i=1;i<=n;++i)
    {
        ans=max(dp[i][i+n-1],ans);
        fk=min(f[i][i+n-1],fk);
    }
    print(fk);putchar('\n');print(ans);
}
/*

*/

数字游戏

也是十分基础有限制的基础水题说明我是个水题都做不来的垃圾

有限制的话多开一维dp[i][j][h]表示[i,j]分为h段的最值;最外两层不变,第三层枚举段数h(本题预处理的h == 1所以从2开始),第四层枚举断点k,范围是[i + h - 1,j - 1];

本题最开始我把sum[]放在初始化dp的双重循环中更新,没有发现初始化需要用到还未更新的sum[],因此错了半天

#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
#include <cstring>
#include <algorithm>
#define rint register int
#define ll long long
using namespace std;
template <typename xxx> inline void read(xxx &x)
{
    int f = 1;x = 0;
    char c = getchar();
    for(; c < '0' || c > '9' ; c = getchar()) if(c=='-') f = -1;
    for(;'0' <= c && c <= '9'; c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
    x *= f;
}
template <typename xxx> inline void print(xxx x)
{
    if(x < 0) {
        putchar('-');
        x = -x;
    }
    if(x > 9) print(x/10);
    putchar(x % 10 + '0');
}
const int inf = 0x7fffffff;
const int maxn = 100100;
const int mod = 1e9+7;
int n,m,n2;
int a[111];
int sum[111];
int f1[111][111][111];// 区间[l,r]分成h段最大
int f2[111][111][111];//最小
inline int deal(int x) {
    return (x % 10 + 10) % 10;
}
int main()
{
    read(n);read(m);n2 = (n<<1);
    for(rint i = 1;i <= n; ++i) {
        read(a[i]);
        a[i] = (a[i]%10 + 10)%10;
        a[i + n] = a[i];
        sum[i] = sum[i - 1] + a[i];
    }
    for(rint i = 1;i <= n; ++i) sum[i + n] = sum[i] + sum[n];
    memset(f2,0x3f,sizeof(f2));
    for(rint i = 1;i <= n2; ++i)
        for(rint j = i;j <= n2; ++j)
            f1[i][j][1] = f2[i][j][1] = deal(sum[j] - sum[i - 1]);
    for(rint len = 1;len <= n; ++len) {
        for(rint i = 1,j = i + len - 1;j <= n2; ++i,++j) {
            for(rint h = 2;h <= m; ++h) {
                for(rint k = i + h - 2;k < j; ++k) {
                    f1[i][j][h] = max(f1[i][j][h],f1[i][k][h - 1] * deal(sum[j] - sum[k]));
                    f2[i][j][h] = min(f2[i][j][h],f2[i][k][h - 1] * deal(sum[j] - sum[k]));
                }
            }
        }
    }
    int maxx = 0,minn = 0x3f3f3f3f;
    for(rint i = 1;i <= n; ++i) {
        maxx = max(maxx,f1[i][i + n - 1][m]);
        minn = min(minn,f2[i][i + n - 1][m]);
    }
    print(minn);putchar('\n');print(maxx);
}
/*
*/

原文地址:https://www.cnblogs.com/Thomastine/p/11739210.html

时间: 2024-08-29 02:20:44

区间dp与环形dp的相关文章

转载+删改:算法讲解之Dynamic Programing —— 区间DP [变形:环形DP]

发现一篇好文,可惜发现有一些地方有排版问题.于是改了一下,并加了一些自己的内容. 原文链接 对区间DP和其变式环形DP的总结. 首先先来例题. 石子归并 题目描述 Description 有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1].问安排怎样的合并顺序,能够使得总合并代价达到最小. 输入描述 Input Description 第一行一个整数n(n<=100) 第二行n个整数w1,w2...wn (wi

洛谷P1063 能量项链(区间DP)(环形DP)

To 洛谷.1063 能量项链 题目描述 在Mars星球上,每个Mars人都随身佩带着一串能量项链.在项链上有N颗能量珠.能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数.并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记.因为只有这样,通过吸盘(吸盘是Mars人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸盘吸收的能量.如果前一颗能量珠的头标记为m,尾标记为r,后一颗能量珠的头标记为r,尾标记为n,则聚合后释放的能量为m*r*n(M

codevs1085数字游戏(环形DP+划分DP )

1085 数字游戏 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 丁丁最近沉迷于一个数字游戏之中.这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易.游戏是这样的,在你面前有一圈整数(一共n个),你要按顺序将其分为m个部分,各部分内的数字相加,相加所得的m个结果对10取模后再相乘,最终得到一个数k.游戏的要求是使你所得的k最大或者最小. 例如,对于下面这圈数字(n=4,m=2): 2

2017.8.15 [Haoi2016]字符合并 区间dp+状压dp

[题目描述] 有一个长度为n的01串,你可以每次将相邻的k个字符合并,得到一个新的字符并获得一定分数.得到的新字符和分数由这k个字符确定.你需要求出你能获得的最大分数. [输入格式] 第一行两个整数n,k. 接下来一行长度为n的01串,表示初始串.输入的的相邻字符之间用一个空格隔开. 接下来2k行,每行一个字符ci和一个整数wi,ci表示长度为k的01串连成二进制后按从小到大顺序得到的第i种合并方案得到的新字符, wi表示对应的第i种方案对应获得的分数. [输出格式] 输出一个整数表示答案. [

石子合并【环形DP】

先放上luogu的石子合并题目链接 这是一道环形DP题,思想和能量项链很像,在预处理过程中的手法跟乘积最大相像. 用一个m[][]数组来存储石子数量,m[i][j]表示从第 i 堆石子到第 j 堆石子的总数. 接下来三重循环 i 表示合并操作的起始位置, j 表示合并操作的终点,也就是把 i 到 j 合并 k表示间断点,即 i 到 j 合并过程中选择k点来作为合并位置 状态转移方程 f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+m[i][k]+m[k+1][j]);

多边形游戏(经典的环形dp)

描述 一个多边形,开始有n个顶点.每个顶点被赋予一个正整数值,每条边被赋予一个运算符"+"或"*".所有边依次用整数从1到n编号. 现在来玩一个游戏,该游戏共有n步: 第1步,选择一条边,将其删除 随后n-1步,每一步都按以下方式操作:(1)选择一条边E以及由E连接着的2个顶点v1和v2: (2)用一个新的顶点取代边E以及由E连接着的2个顶点v1和v2,将顶点v1和v2的整数值通过边E上的运算得到的结果值赋给新顶点. 最后,所有边都被删除,只剩一个顶点,游戏结束.游

HDU6447 YJJ&#39;s Salesman-2018CCPC网络赛-线段树求区间最值+离散化+dp

目录 Catalog Solution: (有任何问题欢迎留言或私聊 && 欢迎交流讨论哦 Catalog Problem:Portal传送门 ?原题目描述在最下面. ?1e5个点,问从(0,0)走到(1e9,1e9)的最大收益. ?当你从(u-1,v-1)走到(u,v)时,你可以获得点(u,v)的权值. Solution: ?十分详细了. ?直接线段树区间最值.当然也可以树状数组,不能st表. ?\(dp[i] = max(query\_max(0,dp[i]-1,1)+val[i] ,

『字符合并 区间dp 状压dp』

字符合并 Description 有一个长度为 n 的 01 串,你可以每次将相邻的 k 个字符合并,得到一个新的字符并获得一定分数.得到的新字符和分数由这 k 个字符确定.你需要求出你能获得的最大分数. Input Format 第一行两个整数n,k.接下来一行长度为n的01串,表示初始串. 接下来2^k行,每行一个字符ci和一个整数wi,ci表示长度为k的01串连成二进制后按从小到大顺序得到的第i种合并方案得到的新字符,wi表示对应的第i种方案对应获得的分数. 1<=n<=300,0<

FZU - 2204 简单环形dp

FZU - 2204 简单环形dp 题目链接 n个有标号的球围成一个圈.每个球有两种颜色可以选择黑或白染色.问有多少种方案使得没有出现连续白球7个或连续黑球7个. 输入 第一行有多组数据.第一行T表示组数.(T <= 20) 每组包含n,表示球的个数.(1 <= n <= 100000) 输出 每组先输出 "Case #x: " (其中x为当前组数) 该行接下来输出方案数.方案数mod 2015. 样例 2 7 1 Case #1: 126 Case #2: 2 思路