【专题】区间dp

1.【nyoj737】石子合并

传送门:点击打开链接

描述    有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。

输入
有多组测试数据,输入到文件结束。
每组测试数据第一行有一个整数n,表示有n堆石子。
接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开
输出
输出总代价的最小值,占单独的一行
样例输入
3
1 2 3
7
13 7 8 16 21 4 18
样例输出
9
239
思路:

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[i][j]),dp[i][j]代表从i到j合并的最小值,sum[i][j]代表从i到j的价值和。

区间dp的循环是从小区间到大区间,根据这一规律写出循环结构。

注意dp[i][i]是0。

代码:

#include<bits/stdc++.h>
using namespace std;
int dp[205][205],a[205],sum[205][205];
int main()
{
    int n,i,j,k;
    while(cin>>n)
    {
        memset(sum,0,sizeof(sum));
        memset(dp,0x3f3f3f3f,sizeof(dp));
        for(i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            dp[i][i]=0;
            sum[i][i]=a[i];
        }
        for(i=1; i<n; i++)
        {
            for(j=i+1; j<=n; j++)
                sum[i][j]+=sum[i][j-1]+a[j];
        }
        for(int len=2; len<=n; len++)
            for(i=1; i<=n-len+1; i++)
            {
                j=i+len-1;
                for(k=i; k+1<=j; k++)
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[i][j]);
            }
        printf("%d\n",dp[1][n]);
    }
    return 0;
}  

2.【nyoj37】回文字符串

传送门:点击打开链接

给你一个字符串,可在任意位置添加字符,最少再添加几个字符,可以使这个字符串成为回文字符串。

样例输入

1
Ab3bd

样例输出

2

思路1:

利用反串找LCA

#include<bits/stdc++.h>
using namespace std;
char s[1005];
int dp[1005][1005];
int main()
{
    int t,i,j;
    cin>>t;
    while(t--)
    {
        scanf("%s",s);
        int n=strlen(s);
        memset(dp,0x3f3f3f3f,sizeof(dp));
        dp[0][0]=0;
        for(i=1; i<n; i++)
            dp[i][i]=dp[i][i-1]=0;
        for(int len=2; len<=n; len++)
            for(i=0; i<=n-len; i++)
            {
                j=i+len-1;
                if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1];
                else dp[i][j]=min(dp[i+1][j]+1,dp[i][j-1]+1);
            }
        printf("%d\n",dp[0][n-1]);
    }
} 

思路2:

s[i]=s[j],dp[i][j]=dp[i+1][j-1];若!=,dp[i][j]=min(dp[i+1][j]+1,dp[i][j-1]+1)

解释一下,假如回文串是「1123」,dp[1][4] = min(dp[1][3],dp[2][4]) + 1,先把「112」看成一个整体,dp[1][3] = 1,就是说再补一个就可以回文,最后加上一个「3」,也就是 +1 操作;「123」同理。

注意dp[i][i]=0,当len=2时,若s[i]=s[j]则dp[i][j]=0,所以初始化dp[i][i-1]也要全部赋为0

#include<bits/stdc++.h>
using namespace std;
char s[1005];
int dp[1005][1005];
int main()
{
    int t,i,j;
    cin>>t;
    while(t--)
    {
        scanf("%s",s);
        int n=strlen(s);
        memset(dp,0x3f3f3f3f,sizeof(dp));
        dp[0][0]=0;
        for(i=1; i<n; i++)
            dp[i][i]=dp[i][i-1]=0;
        for(int len=2; len<=n; len++)
            for(i=0; i<=n-len; i++)
            {
                j=i+len-1;
                if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1];
                else dp[i][j]=min(dp[i+1][j]+1,dp[i][j-1]+1);
            }
        printf("%d\n",dp[0][n-1]);
    }
} 

3.【nyoj15】括号匹配(二)

传送门:点击打开链接

描述给你一个字符串,里面只包含"(",")","[","]"四种符号,请问你需要至少添加多少个括号才能使这些括号匹配起来。
如:
[]是匹配的
([])[]是匹配的
((]是不匹配的
([)]是不匹配的

输入
第一行输入一个正整数N,表示测试数据组数(N<=10)
每组测试数据都只有一行,是一个字符串S,S中只包含以上所说的四种字符,S的长度不超过100
输出
对于每组测试数据都输出一个正整数,表示最少需要添加的括号的数量。每组测试输出占一行
样例输入
4
[]
([])[]
((]
([)]
样例输出
0
0
3
2
思路:
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]),特殊情况当s[i]与s[j]匹配时,dp[i][j]=min(dp[i][j],dp[i+1][j-1])
注意当截取长度为2且s[i]与s[j]匹配时,不需要添加符号,所以初始化dp[i][i-1]=0,dp[i][i]=1。

#include<bits/stdc++.h>
using namespace std;
char s[105];
int dp[105][105];
int main()
{
    int t,i,j,k;
    cin>>t;
    while(t--)
    {
        scanf("%s",s);
        int n=strlen(s);
        memset(dp,0x3f3f3f3f,sizeof(dp));
        dp[0][0]=1;
        for(i=1; i<n; i++)
        {
            dp[i][i]=1;
            dp[i][i-1]=0;
        }
        for(int len=2; len<=n; len++)
            for(i=0; i<=n-len; i++)
            {
                j=i+len-1;
                if(s[i]==‘[‘&&s[j]==‘]‘||s[i]==‘(‘&&s[j]==‘)‘)
                    dp[i][j]=dp[i+1][j-1];
                for(k=i; k+1<=j; k++)//这里不是else
                    dp[i][j]=min(dp[i][k]+dp[k+1][j],dp[i][j]);
            }
        printf("%d\n",dp[0][n-1]);
    }
}

4.【nyoj746】整数划分(四)

传送门:点击打开链接

给出两个整数 n , m ,要求在 n 中加入m - 1 个乘号,将n分成m段,求出这m段的最大乘积

输入
第一行是一个整数T,表示有T组测试数据
接下来T行,每行有两个正整数 n,m ( 1<= n < 10^19, 0 < m <= n的位数);
输出
输出每组测试样例结果为一个整数占一行
样例输入
2
111 2
1111 2
样例输出
11
121

思路:

由于最大的划分所有的乘号都是固定的,可以从前到后以此找出所有乘号的位置,找出最优子结构(从结论入手向前拆分问题)。

dp[i][j]代表0到i个数字中添加j个乘号,要求dp[n][m]即求dp[k][m-1]*a[k+1][i],应用到所有dp[i][j]=max(dp[i][j],dp[k][j-1]*a[k+1][i]),a[i][j]是从i到j的组合数。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL dp[25][25],a[25][25];
char s[25];
int main()
{
    LL n,m,t,i,j,k;
    cin>>t;
    while(t--)
    {
        scanf("%s%lld",s,&m);
        n=strlen(s);
        memset(dp,0,sizeof(dp));
        for(i=0; i<n; i++)
            a[i][i]=s[i]-‘0‘;
        for(i=0; i<n-1; i++)
            for(j=i+1; j<n; j++)
                a[i][j]=a[i][j-1]*10+(s[j]-‘0‘);
        for(i=0; i<n; i++)
            dp[i][0]=a[0][i];
        for(j=1; j<=m-1; j++)
            for(i=1; i<n; i++)
                for(k=0; k+1<=i; k++)
                    dp[i][j]=max(dp[i][j],dp[k][j-1]*a[k+1][i]);
        printf("%lld\n",dp[n-1][m-1]);
    }
    return 0;
}  

原文地址:https://www.cnblogs.com/lesroad/p/8440010.html

时间: 2024-10-07 15:30:12

【专题】区间dp的相关文章

「kuangbin带你飞」专题二十二 区间DP

layout: post title: 「kuangbin带你飞」专题二十二 区间DP author: "luowentaoaa" catalog: true tags: - kuangbin - 区间DP - 动态规划 传送门 B.LightOJ - 1422 Halloween Costumes 题意 按顺序参加舞会,参加一个舞会要穿一种衣服,可以在参加完一个舞会后套上另一个衣服再去参加舞会,也可以在参加一个舞会的时候把外面的衣服脱了,脱到合适的衣服,但是脱掉的衣服不能再穿,参加完

区间dp专题练习

区间dp专题练习 题意 1.Equal Sum Partitions ? 这嘛东西,\(n^2\)自己写去 \[\ \] \[\ \] 2.You Are the One 感觉自己智力被吊打 \(dp[i][j]\)表示 , 对于当前的一个空栈 , \(i\)到\(j\)这一段都出栈的最小花费 显然是长得一副区间(诡)dp(异)的样子 , 如何转移呢?(建议自己想想吧) 对于一个\(dp[i][j]\),因为这个\(i\)必须是最先入栈的 , 所以我们可以枚举它的出栈时间\(k\) , 那么总贡

QBXT 2018春季DP&amp;图论班 2018.5.3 --- 区间DP专题

本文题目等来自北京大学张浩威学长的PPT. 1.区间DP:解决有关两个或以上的区间的合并或删除的问题(最大/小次数/价值.方案总数.可行性等). 2.石子合并: 有n堆石子排成一排,第i堆石子的个数为ai.每次可以将相邻两堆合并成一堆.合并的代价为两堆石子的石子个数之和.设计方案要求代价之和最小. 状态:dp[l][r]表示只考虑区间l~r的石子,将它们合并的最小代价. 状态转移:dp[l][r]=min{dp[l][r],dp[l][k]+dp[k+1][r]+s[r]-s[l-1]} l~r

专题训练之区间DP

例题:以下例题部分的内容来自https://blog.csdn.net/my_sunshine26/article/details/77141398 一.石子合并问题 1.(NYOJ737)http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=737 分析:我们dp[i][j]来表示合并第i堆到第j堆石子的最小代价.那么状态转移方程为dp[i][j]=min(dp[i][k]+dp[k+1][j]+w[i][j])  (s[i][j-1]<=k<

UVA 10003 Cutting Sticks 区间DP+记忆化搜索

UVA 10003 Cutting Sticks+区间DP 纵有疾风起 题目大意 有一个长为L的木棍,木棍中间有n个切点.每次切割的费用为当前木棍的长度.求切割木棍的最小费用 输入输出 第一行是木棍的长度L,第二行是切割点的个数n,接下来的n行是切割点在木棍上的坐标. 输出切割木棍的最小费用 前话-区间dp简单入门 区间dp的入门下面博客写的非常好,我就是看的他们博客学会的,入门简单,以后的应用就得靠自己了. https://blog.csdn.net/qq_41661809/article/d

uva 10003 Cutting Sticks 简单区间dp

// uva 10003 Cutting Sticks 区间dp // 经典的区间dp // dp(i,j)表示切割小木棍i-j所需要的最小花费 // 则状态转移为dp(i,j) = min{dp(i,k) + dp(k,j) + a[j]-a[i]) // 其中k>i && k<j // a[j] - a[i] 为第一刀切割的代价 // a[0] = 0,a[n+1] = L; // dp数组初始化的时候dp[i][i+1]的值为 0,这表示 // 每一段都已经是切割了的,不

黑书例题 Fight Club 区间DP

题目可以在bnuoj.soj等OJ上找到. 题意: 不超过40个人站成一圈,只能和两边的人对战.给出任意两人对战的输赢,对于每一个人,输出是否可能是最后的胜者. 分析: 首先序列扩展成2倍,破环成链. dp[i][j]表示i和j能够相遇对打,那么dp[i][i+n]为真代表可以成为最后胜者. 枚举中间的k,若i和j都能和k相遇,且i和j至少一人能打赢k,那么i和j可以相遇. 复杂度o(n^3) 1 #include<cstdio> 2 #include<cstring> 3 usi

算法复习——区间dp

感觉对区间dp也不好说些什么直接照搬讲义了2333 例题: 1.引水入城(洛谷1514) 这道题先开始看不出来到底和区间dp有什么卵关系···· 首先肯定是bfs暴力判一判可以覆盖到哪些城市····无解直接输出···有解得话就要想想了···· 这道题关键是要发现··如果一个蓄水池所在城市可以覆盖到一些沙漠城市···那么这些沙漠城市肯定是一段····不然假设有一个城市是断开的而两边都被同一个蓄水池流出的水覆盖,这个城市四周的城市都肯定比它矮···(不理解举个反例吧···反正我举不出来)···然后就

合并石子 区间dp水题

合并石子 链接: nyoj 737 描述: 有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆.求出总的代价最小值. tags:最基本的区间dp,这题范围小,如果n大一些,还是要加个平行四边行优化. #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring&g