您的位置:首页 > 其它

区间dp模型(石子归并,括号匹配,整数划分)入门经典三道题

2016-09-13 21:03 423 查看
区间dp顾名思义就是在一个区间上进行的一系列动态规划。对一些经典的区间dp总结在这里。

1) 石子归并问题

题目链接:http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=737

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

分析:要求n个石子归并,我们根据dp的思想划分成子问题,先求出每两个合并的最小代价,然后每三个的最小代价,依次知道n个。

定义状态dp [ i ] [ j ]为从第i个石子到第j个石子的合并最小代价。

那么dp [ i ] [ j ] = min(dp [ i ] [ k ] + dp [ k+1 ] [ j ])

那么我们就可以从小到大依次枚举让石子合并,直到所有的石子都合并。

这个问题可以用到平行四边形优化,用一个s【i】【j】=k 表示区间 i---j 从k点分开才是最优的,这样的话我们就可以优化掉一层复杂度,变为O(n^2).

代码:

[cpp]
view plain
copy
print?





#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 210
int dp

,sum
;
int main()
{
int n;
while(~scanf("%d",&n))
{
int a
;sum[0]=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
memset(dp,0,sizeof(dp));
int i,j,l,k;
for(l = 2; l <= n; ++l)
{
for(i = 1; i <= n - l + 1; ++i)
{
j = i + l - 1;
dp[i][j] = 2100000000;
for(k = i; k < j; ++k)
{
dp[i][j] = std::min(dp[i][j],dp[i][k] + dp[k + 1][j] + sum[j] - sum[i-1]);
}
}
}
printf("%d\n", dp[1]
);
}
return 0;
}

平行四边形优化代码:

[cpp]
view plain
copy
print?





#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 210
int dp

,sum
,s

;
int main()
{
int n;
while(~scanf("%d",&n))
{
int a
;sum[0]=0;
memset(s,0,sizeof(s));
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
s[i][i]=i;
sum[i]=sum[i-1]+a[i];
}
memset(dp,0,sizeof(dp));
int i,j,l,k;
for(l = 2; l <= n; ++l)
{
for(i = 1; i <= n - l + 1; ++i)
{
j = i + l - 1;
dp[i][j] = 2100000000;
for(k = s[i][j-1]; k <= s[i+1][j]; ++k)
{
if(dp[i][j]>dp[i][k] + dp[k + 1][j] + sum[j] - sum[i-1])
{
dp[i][j]=dp[i][k] + dp[k + 1][j] + sum[j] - sum[i-1];
s[i][j]=k;
}
}
}
}
printf("%d\n", dp[1]
);
}
return 0;
}

2)括号匹配

题目链接:

poj2955,http://poj.org/problem?id=2955

nyoj 15 http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=15
描述:给出一串的只有‘(’ ‘)’ '[‘ ']'四种括号组成的串,让你求解需要最少添加括号数让串中的所有括号完全匹配。

分析:我们求出这个串的最大匹配,然后串的总长度-最大匹配就是答案。

方法1:首先能想到的是转化成LCS(最长公共子序列),枚举中间点,求所有的LCS中的最大值 * 2就是最大匹配。但是复杂度较高,光LCS一次就O(n^2)的复杂度。

方法2:

首先考虑怎么样定义dp让它满足具有通过子结构来求解、

定义dp [ i ] [ j ] 为串中第 i 个到第 j 个括号的最大匹配数目

那么我们假如知道了 i 到 j 区间的最大匹配,那么i+1到 j+1区间的是不是就可以很简单的得到。

那么 假如第 i 个和第 j 个是一对匹配的括号那么dp [ i ] [ j ] = dp [ i+1 ] [ j-1 ] + 2 ;

那么我们只需要从小到大枚举所有 i 和 j 中间的括号数目,然后满足匹配就用上面式子dp,然后每次更新dp [ i ] [ j ]为最大值即可。

更新最大值的方法是枚举 i 和 j 的中间值,然后让 dp[ i ] [ j ] = max ( dp [ i ] [ j ] , dp [ i ] [ f ] + dp [ f+1 ] [ j ] ) ;

如果要求打印路径,即输出匹配后的括号。见http://blog.csdn.net/y990041769/article/details/24238547详细讲解

代码:

[cpp]
view plain
copy
print?





#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;
const int N = 120;
int dp

;
int main()
{
string s;
while(cin>>s)
{
if(s=="end")
break;
memset(dp,0,sizeof(dp));
for(int i=1;i<s.size();i++)
{
for(int j=0,k=i;k<s.size();j++,k++)
{
if(s[j]=='('&&s[k]==')' || s[j]=='['&&s[k]==']')
dp[j][k]=dp[j+1][k-1]+2;
for(int f=j;f<k;f++)
dp[j][k]=max(dp[j][k],dp[j][f]+dp[f+1][k]);
}
}
cout<<dp[0][s.size()-1]<<endl;
}
return 0;
}

3)整数划分问题

题目链接:nyoj746 http://acm.nyist.net/JudgeOnline/problem.php?pid=746

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

分析:根据区间dp的思想,我们定义dp [ i ] [ j ]为从开始到 i 中加入 j 个乘号得到的最大值。

那么我们可以依次计算加入1----m-1个乘号的结果

而每次放入x个乘号的最大值只需枚举第x个乘号的放的位置即可

dp [ i ] [ j ] = MAX (dp [ i ] [ j ] , dp [ k ] [ j-1 ] * a [ k+1 ] [ i ] ) ;

代码:

[cpp]
view plain
copy
print?





#include <cstdio>
#include <cstring>
#define MAX(a,b) a>b?a:b
long long a[20][20];
long long dp[25][25];
int main()
{
int T,m;
scanf("%d",&T);
getchar();
while(T--)
{
char s[22];
scanf("%s",s+1);
scanf("%d",&m);
int l=strlen(s),ok=1;
memset(a,0,sizeof(a));
for(int i=1;i<l;i++)
{
if(s[i]=='0')
ok=0;
for(int j=i;j<l;j++)
{
a[i][j]=a[i][j-1]*10+(s[j]-'0');
}
}
if(ok==0&&l-1==m||l-1<m)
{
printf("0\n");continue;
}
long long x,ans;
memset(dp,0,sizeof(dp));
for(int i=0;i<l;i++)
dp[i][1]=a[1][i];
ans=0;
if(m==1)
ans=dp[l-1][1];
for(int j=2;j<=m;j++)
{
for(int i=j;i<l;i++)
{
ans=a[i][i];
for(int k=1;k<i;k++)
{
dp[i][j]=MAX(dp[i][j],dp[k][j-1]*a[k+1][i]);
}
}
}
printf("%lld\n",dp[l-1][m]);
}
return 0;
}

NYOJ 737:http://acm.nyist.net/JudgeOnline/problem.php?pid=737

代码:

<code class="hljs cpp has-numbering"><span class="hljs-preprocessor">#include <cstdio></span>
<span class="hljs-preprocessor">#include <iostream></span>
<span class="hljs-preprocessor">#include <cstring></span>
<span class="hljs-preprocessor">#define sf scanf</span>
<span class="hljs-preprocessor">#define pf printf</span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>;
<span class="hljs-keyword">typedef</span> <span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> LL;
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> maxn = <span class="hljs-number">205</span>;
<span class="hljs-comment">/*
题意: N堆石子排成一排 每次可以合并相邻的两堆石子 合并的代价是两堆石子的数量的和
在经过N - 1次合并和,N堆石子合并为一堆石子,问最小的代价是多少
DP[i][j] 表示将第i堆和第j堆合并为一堆的最小花费
那么DP[i][j] 就可以表示成 DP[i][j] = min{DP[i][k] + DP[k + 1][j] + COST((i ~ k):(k + 1 ~ j))}

*/</span>
<span class="hljs-keyword">const</span> LL INF = <span class="hljs-number">0x3f3f3f3f</span>;
LL DP[maxn][maxn];
LL A[maxn];
LL DPS(<span class="hljs-keyword">int</span> l,<span class="hljs-keyword">int</span> r){
<span class="hljs-keyword">if</span>(DP[l][r] != -<span class="hljs-number">1</span>) <span class="hljs-keyword">return</span> DP[l][r];
LL& ans = DP[l][r];
ans = INF;
LL L = <span class="hljs-number">0</span>,R = <span class="hljs-number">0</span>;
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i = l;i <= r;++i) R += A[i];
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> k = l;k < r;++k){
L += A[k],R -= A[k];
ans = min(ans,L + R + DPS(l,k) + DPS(k + <span class="hljs-number">1</span>,r));
}
<span class="hljs-keyword">return</span> ans;
}
<span class="hljs-keyword">int</span> main(){
<span class="hljs-keyword">int</span> n;
<span class="hljs-keyword">while</span>( ~sf(<span class="hljs-string">"%d"</span>,&n) ){
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>;i < n;++i) sf(<span class="hljs-string">"%lld"</span>,A + i);
<span class="hljs-built_in">memset</span>(DP,-<span class="hljs-number">1</span>,<span class="hljs-keyword">sizeof</span>(DP));
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>;i < n;++i) DP[i][i] = <span class="hljs-number">0</span>;
DPS(<span class="hljs-number">0</span>,n - <span class="hljs-number">1</span>);
pf(<span class="hljs-string">"%lld\n"</span>,DP[<span class="hljs-number">0</span>][n - <span class="hljs-number">1</span>]);
}
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li></ul>

POJ 2955:http://poj.org/problem?id=2955

代码:

<code class="hljs cpp has-numbering"><span class="hljs-preprocessor">#include <cstdio></span>
<span class="hljs-preprocessor">#include <iostream></span>
<span class="hljs-preprocessor">#include <cstring></span>
<span class="hljs-preprocessor">#define sf scanf</span>
<span class="hljs-preprocessor">#define pf printf</span>
<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>;
<span class="hljs-comment">/*
题意:给一个只包含‘[’,‘]’,‘(’,‘)’的字符串求在这个字符串的一个最长的子串 是一个合法括号字符
DP[i][j] 表示将i 到 j 变为合法字符串要删除的最少字符数
那么 DP[i][j] = min{DP[i][k] + DP[k + 1][j]};check(s[i],s[j]) DP[i][j] = min(DP[i][j],DP[i + 1][j - 1]);
*/</span>
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> INF = <span class="hljs-number">0x3f3f3f3f</span>;
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> maxn = <span class="hljs-number">105</span>;
<span class="hljs-keyword">int</span> DP[maxn][maxn];
<span class="hljs-keyword">char</span> s[maxn];
<span class="hljs-keyword">int</span> DPS(<span class="hljs-keyword">int</span> l,<span class="hljs-keyword">int</span> r){
<span class="hljs-keyword">if</span>(~DP[l][r]) <span class="hljs-keyword">return</span> DP[l][r];
<span class="hljs-keyword">int</span>& ans = DP[l][r];
ans = INF;
<span class="hljs-keyword">if</span>(s[l] == <span class="hljs-string">'('</span> && s[r] == <span class="hljs-string">')'</span> || s[l] == <span class="hljs-string">'['</span> && s[r] == <span class="hljs-string">']'</span>) ans = min(ans,DPS(l + <span class="hljs-number">1</span>,r - <span class="hljs-number">1</span>));
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> k = l;k < r;++k){
ans = min(ans,DPS(l,k) + DPS(k + <span class="hljs-number">1</span>,r));
}
<span class="hljs-keyword">return</span> ans;
}

<span class="hljs-keyword">int</span> main(){
<span class="hljs-keyword">while</span>(sf(<span class="hljs-string">"%s"</span>,s)){
<span class="hljs-keyword">if</span>(s[<span class="hljs-number">0</span>] == <span class="hljs-string">'e'</span>) <span class="hljs-keyword">break</span>;
<span class="hljs-keyword">int</span> len = <span class="hljs-built_in">strlen</span>(s);
<span class="hljs-built_in">memset</span>(DP,-<span class="hljs-number">1</span>,<span class="hljs-keyword">sizeof</span>(DP));
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>;i < len;++i){
DP[i][i] = <span class="hljs-number">1</span>;
DP[i + <span class="hljs-number">1</span>][i] = <span class="hljs-number">0</span>;
}
pf(<span class="hljs-string">"%d\n"</span>,len - DPS(<span class="hljs-number">0</span>,len - <span class="hljs-number">1</span>));
}
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li></ul>

NYOJ 746:http://acm.nyist.net/JudgeOnline/problem.php?pid=746

<code class="hljs cpp has-numbering"><span class="hljs-preprocessor">#include <cstdio></span>
<span class="hljs-preprocessor">#include <iostream></span>
<span class="hljs-preprocessor">#include <queue></span>
<span class="hljs-preprocessor">#include <cstring></span>
<span class="hljs-preprocessor">#define sf scanf</span>
<span class="hljs-preprocessor">#define pf printf</span>
<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>;
<span class="hljs-keyword">typedef</span> <span class="hljs-keyword">long</span> <span class="hljs-keyword">long</span> <span class="hljs-keyword">int</span> LL;
<span class="hljs-comment">/*  分类 区间DP入门题
题意: 要求将一个整数N 划分成M段 使这M段的乘积最大

现在我们将整数 看作一个由数字组成的字符串 那么
DP[l][r][m] 表示将子串(l - r)划分成M段的最大乘积的值
那么DP[l][r][m] max{DP[l][k][p] * DP[k + 1][r][q]}  (k >= l ^ k < r ^ (p + q) == m)

*/</span>

<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> maxn = <span class="hljs-number">20</span>;
LL DP[maxn][maxn][maxn];
LL G[maxn],F[maxn];
<span class="hljs-keyword">int</span> len;
<span class="hljs-keyword">char</span> s[maxn];
LL DPS(<span class="hljs-keyword">int</span> l,<span class="hljs-keyword">int</span> r,<span class="hljs-keyword">int</span> m){
<span class="hljs-keyword">if</span>(~DP[l][r][m]) <span class="hljs-keyword">return</span> DP[l][r][m];
LL& ans = DP[l][r][m] = <span class="hljs-number">0</span>;
<span class="hljs-keyword">if</span>(r - l + <span class="hljs-number">1</span> < m) ans = <span class="hljs-number">0</span>;
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(m == <span class="hljs-number">1</span>){
ans = G[l] / F[r + <span class="hljs-number">1</span>];
}
<span class="hljs-keyword">else</span> <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> k = l;k < r;++k){
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> p = <span class="hljs-number">1</span>;p <= m;++p){
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> q = <span class="hljs-number">1</span>;q <= m;++q){
<span class="hljs-keyword">if</span>(p + q == m){
ans = max(ans,DPS(l,k,p) * DPS(k + <span class="hljs-number">1</span>,r,q));
}
}
}
}
<span class="hljs-comment">//    pf("%d %d %d %lld\n",l,r,m,ans);</span>
<span class="hljs-keyword">return</span> ans;
}

<span class="hljs-keyword">int</span> main(){
<span class="hljs-keyword">int</span> T;sf(<span class="hljs-string">"%d"</span>,&T);
<span class="hljs-keyword">while</span>( T-- ){
<span class="hljs-built_in">memset</span>(DP,-<span class="hljs-number">1</span>,<span class="hljs-keyword">sizeof</span>(DP));
<span class="hljs-keyword">int</span> n,m;
sf(<span class="hljs-string">"%s %d"</span>,s,&m);
len = n = <span class="hljs-built_in">strlen</span>(s);
F[len] = <span class="hljs-number">1</span>;
G[len] = <span class="hljs-number">0</span>;
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i = len - <span class="hljs-number">1</span>;~i;--i) {
F[i] = F[i + <span class="hljs-number">1</span>] * <span class="hljs-number">10</span>;
G[i] = G[i + <span class="hljs-number">1</span>] + (s[i] - <span class="hljs-string">'0'</span>) * F[i + <span class="hljs-number">1</span>];
}
<span class="hljs-comment">//        pf("%lld\n",G[0] / F[1]);</span>
pf(<span class="hljs-string">"%lld\n"</span>,DPS(<span class="hljs-number">0</span>,n - <span class="hljs-number">1</span>,m));
}
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  动态规划