专题训练之区间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<=s[i+1][j])

其中w[i][j]表示把两部分合并起来的代价,即从第i堆到第j堆石子个数的和,为了方便查询,我们可以用sum[i]表示从第1堆到第i堆的石子个数和,那么w[i][j]=sum[j]-sum[i-1].

用s[i][j]表示区间[i,j]中的最优分割点,那么第三重循环可以从[i,j-1)优化到【s[i][j-1],s[i+1][j]】

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 const int maxn=210;
 7 const ll inf=1e18;
 8 ll dp[maxn][maxn];
 9 ll sum[maxn],a[maxn];
10 int s[maxn][maxn];
11
12 int main()
13 {
14     int n,i,j,k,x,y,z,len;
15     while ( scanf("%d",&n)!=EOF )
16     {
17         for ( i=1;i<=n;i++ )
18         {
19             for ( j=1;j<=n;j++ ) dp[i][j]=inf;
20             dp[i][i]=0;
21             s[i][i]=i;
22         }
23         sum[0]=0;
24         for ( i=1;i<=n;i++ )
25         {
26             scanf("%lld",&a[i]);
27             sum[i]=a[i]+sum[i-1];
28         }
29         for ( len=2;len<=n;len++ )
30         {
31             for ( i=1;i<=n;i++ )
32             {
33                 j=len+i-1;
34                 if ( j>n ) break;
35                 for ( k=s[i][j-1];k<=s[i+1][j];k++ )
36                 {
37                     if ( dp[i][j]>dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1] )
38                     {
39                         dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
40                         s[i][j]=k;
41                     }
42                 }
43             }
44         }
45         printf("%lld\n",dp[1][n]);
46     }
47     return 0;
48 } 

NYOJ737

2.(HDOJ3506)http://acm.hdu.edu.cn/showproblem.php?pid=3506

题意:上一题的升级版,将上一层的线性变成一个圈。这时候我们只需要将N变成n=2*N-1即可,最后ans=min(dp[i][i+n-1])

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 const int maxn=2010;
 7 const ll inf=1e18;
 8 ll dp[maxn][maxn];
 9 ll sum[maxn],a[maxn];
10 int s[maxn][maxn];
11
12 int main()
13 {
14     int n,i,j,k,x,y,z,len,N;
15     ll ans;
16     while ( scanf("%d",&N)!=EOF )
17     {
18         n=2*N-1;
19         for ( i=1;i<=n;i++ )
20         {
21             for ( j=1;j<=n;j++ ) dp[i][j]=inf;
22             dp[i][i]=0;
23             s[i][i]=i;
24         }
25         sum[0]=0;
26         for ( i=1;i<=N;i++ )
27         {
28             scanf("%lld",&a[i]);
29             sum[i]=a[i]+sum[i-1];
30         }
31         for ( i=1;i<N;i++ ) sum[i+N]=a[i]+sum[i+N-1];
32         for ( len=2;len<=N;len++ )
33         {
34             for ( i=1;i<=n;i++ )
35             {
36                 j=len+i-1;
37                 if ( j>n ) break;
38                 for ( k=s[i][j-1];k<=s[i+1][j];k++ )
39                 {
40                     if ( dp[i][j]>dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1] )
41                     {
42                         dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
43                         s[i][j]=k;
44                     }
45                 }
46             }
47         }
48         ans=inf;
49         for ( i=1;i<=N;i++ )
50         {
51             j=i+N-1;
52             ans=min(ans,dp[i][j]);
53         }
54         printf("%lld\n",ans);
55     }
56     return 0;
57 } 

HDOJ3506

二、括号匹配问题

1.(POJ2955)http://poj.org/problem?id=2955

题意:给出一个的只有‘(‘,‘)‘,‘[‘,‘]‘四种括号组成的字符串,求最多有多少个括号满足题目里所描述的完全匹配。

分析:用dp[i][j]表示区间[i,j]里最大完全匹配数。只要得到了dp[i][j],那么就可以得到dp[i-1][j+1]  dp[i-1][j+1]=dp[i][j]+(s[i-1]于s[j+1]匹配?2:0).

然后利用状态转移方程更新一下区间最优解即可。dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j])

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 const int maxn=105;
 7 char s[maxn];
 8 ll dp[maxn][maxn];
 9
10 int main()
11 {
12     int n,i,j,k,x,y,z,len;
13     while ( scanf("%s",s+1)!=EOF && s[1]!=‘e‘ )
14     {
15         n=strlen(s+1);
16         memset(dp,0,sizeof(dp));
17         for ( len=2;len<=n;len++ )
18         {
19             for ( i=1;i<=n;i++ )
20             {
21                 j=i+len-1;
22                 if ( j>n ) break;
23                 if ( (s[i]==‘(‘&&s[j]==‘)‘) || (s[i]==‘[‘&&s[j]==‘]‘) ) dp[i][j]=dp[i+1][j-1]+2;
24                 for ( k=i;k<j;k++ ) dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);
25             }
26         }
27         printf("%lld\n",dp[1][n]);
28     }
29     return 0;
30 } 

POJ2955

2.(NYOJ15)http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=15

分析:最少添加的括号数=总括号-最大匹配的括号数,代码于上一题基本一致

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 const int maxn=105;
 7 char s[maxn];
 8 ll dp[maxn][maxn];
 9
10 int main()
11 {
12     int n,i,j,k,x,y,z,len,T;
13     scanf("%d",&T);
14     while ( T-- )
15     {
16         scanf("%s",s+1);
17         n=strlen(s+1);
18         memset(dp,0,sizeof(dp));
19         for ( len=2;len<=n;len++ )
20         {
21             for ( i=1;i<=n;i++ )
22             {
23                 j=i+len-1;
24                 if ( j>n ) break;
25                 if ( (s[i]==‘(‘&&s[j]==‘)‘) || (s[i]==‘[‘&&s[j]==‘]‘) ) dp[i][j]=dp[i+1][j-1]+2;
26                 for ( k=i;k<j;k++ ) dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);
27             }
28         }
29         printf("%lld\n",n-dp[1][n]);
30     }
31     return 0;
32 } 

NYOJ15

三、整数划分问题

1.(NYOJ746)http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=746

分析:用dp[i][j]表示从第一位到第i位共插入j个乘号后乘积的最大值。根据区间DP的思想我们可以从插入较少乘号的结果算出插入较多乘号的结果。

方法是当我们要放第j的乘号时枚举放的位置。状态转移方程为dp[i][j]=max(dp[i][j],dp[k][j-1]*num[k+1][i])。其中num[i][j]表示从s[i]到s[j]这段连续区间代表的数值。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 const int maxn=20;
 7 ll dp[maxn][maxn];
 8 ll num[maxn][maxn];
 9
10 int main()
11 {
12     int T,n,m,i,j,k,x,y,z;
13     char s[maxn];
14     scanf("%d",&T);
15     while ( T-- )
16     {
17         scanf("%s%d",s+1,&m);
18         n=strlen(s+1);
19         memset(dp,0,sizeof(dp));
20         for ( i=1;i<=n;i++ )
21         {
22             num[i][i]=s[i]-‘0‘;
23             for ( j=i+1;j<=n;j++ ) num[i][j]=num[i][j-1]*10+s[j]-‘0‘;
24         }
25         for ( i=1;i<=n;i++ ) dp[i][0]=num[1][i];
26         for ( j=1;j<m;j++ )
27         {
28             for ( i=1;i<=n;i++ )
29             {
30                 for ( k=1;k<i;k++ ) dp[i][j]=max(dp[i][j],dp[k][j-1]*num[k+1][i]);
31             }
32         }
33         printf("%lld\n",dp[n][m-1]);
34     }
35     return 0;
36 }

NYOJ746

原文地址:https://www.cnblogs.com/HDUjackyan/p/9123199.html

时间: 2024-10-10 02:05:04

专题训练之区间DP的相关文章

专题训练之数位DP

推荐以下一篇博客:https://blog.csdn.net/wust_zzwh/article/details/52100392 1.(HDOJ2089)http://acm.hdu.edu.cn/showproblem.php?pid=2089 分析:裸模板题 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long

「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\) , 那么总贡

dp暑假专题 训练记录

A 回文串的最小划分 题意:给出长度不超过1000的字符串,把它分割成若干个回文字串,求能分成的最少字串数. #include <iostream> #include <cstdio> #include <string.h> #include <string> using namespace std; const int mod = 1e9 + 7; const int maxn = 1000 + 5; const int INF = 0x3f3f3f3f;

【专题】区间dp

1.[nyoj737]石子合并 传送门:点击打开链接 描述    有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆.求出总的代价最小值. 输入 有多组测试数据,输入到文件结束.每组测试数据第一行有一个整数n,表示有n堆石子.接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开 输出 输出总代价的最小值,占单独的一行 样例输入 3 1 2 3

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

uva live 4394 String painter 区间dp

// uva live 4394 String painter // // 这一题是训练指南上dp专题的习题,初看之下认为仅仅是稍微复杂了一点 // 就敲阿敲阿敲,两个半小时后,发现例子过了.然而自己给出的数据跪了 // 交了也wa了,才发现,自己的方法是有问题的,假设是将两个串同一时候考虑 // 的话,比方: dp[i][j] 表示从i到j,s串刷成目标b串所须要的最小的花费 // 然后依据区间的端点的字符特点,进行状态转移,然而可能是我太搓了, // 发现这种状态转移是不正确的.比方edc和

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

uva10891 Game of Sum 博弈区间dp

// uva10891 Game of Sum // 这是在训练指南上看到的一题,啃了很久很久,到现在有 // 一定的动态规划的基础,然而博弈性的东西依然不会 // // // 一开始想的是dp(i,j)表示在i,j段取得最大值 // dp(i,j) = max(dp(i,i),d(i.i+1),...d(i,j-1),d(j-1,j),d(j-2,j)...d(i+1)(j);,sum(i,j)) // 然而,连样例都没过... // // 把表打出来看了一下,发现只是每次取了当前的最大值,根