首页 > 代码库 > 区间DP总结

区间DP总结

做了一些区间DP的题目,总结如下

 

1.Multiplication Puzzle

原题地址:http://poj.org/problem?id=1651

题意:

给定一个序列,可以依次从序列中取走除了左右两端点之外的元素,每次取走一个元素,获得该元素乘以它左右两边元素乘积的点数,求可能的最小点数

题解:

枚举区间中最后一个被取走的元素,实现区间的分割,也就是状态的转移。

详细的解题报告

 

2.Dire Wolf

原题地址:http://acm.split.hdu.edu.cn/showproblem.php?pid=5115

题意:

有一群狼站成一排,每个狼有基础的攻击力,同时有一个辅助攻击力,每个狼的总攻击力是基础攻击力加上相邻的狼给予的辅助攻击力之和,求杀狼的次序使得收到的总攻击力之和最小。

题解:和上一题非常相似

技术分享
#include<bits/stdc++.h>#define clr(x,y) memset((x),(y),sizeof(x))using namespace std;typedef long long LL;const int maxn=200;const int inf=1e9;int A[maxn+5];int B[maxn+5];int dp[maxn+5][maxn+5];int n;int DP(int l,int r){    if (dp[l][r]!=-1) return dp[l][r];    if (l==r-1) return dp[l][r]=0;    dp[l][r]=inf;    for (int k=l+1;k<=r-1;++k)    {        int tmp=A[k]+B[l]+B[r];        dp[l][r]=min(dp[l][r],DP(l,k)+DP(k,r)+tmp);    }    return dp[l][r];}void solve(int C){    A[0]=0;    A[n+1]=0;    B[0]=0;    B[n+1]=0;    clr(dp,-1);    int ans=DP(0,n+1);    printf("Case #%d: %d\n",C,ans);}int main(void){    #ifdef ex    freopen ("../in.txt","r",stdin);    //freopen ("../out.txt","w",stdout);    #endif    int T;    scanf("%d",&T);    for (int Case=1;Case<=T;++Case)    {        scanf("%d",&n);        for (int i=1;i<=n;++i) scanf("%d",&A[i]);        for (int i=1;i<=n;++i) scanf("%d",&B[i]);        solve(Case);    }}
View Code

 

3.男神的礼物

原题地址:http://acm.uestc.edu.cn/#/problem/show/1131

题意:略

题解:略

技术分享
#include<bits/stdc++.h>#define clr(x,y) memset((x),(y),sizeof(x))using namespace std;typedef long long LL;const int maxn=100;const int inf=1e6;int n;int dp[maxn+5][maxn+5];int A[maxn+5];int sum[maxn+5];void solve(){    clr(dp,0);    clr(sum,0);    for (int i=1;i<=n;++i)        sum[i]=(sum[i-1]+A[i])%100;    for (int i=n;i>=1;--i)    {        for (int j=i;j<=n;++j)        {            dp[i][j]=inf;            if (i==j) dp[i][j]=0;            for (int k=i;k<=j-1;++k)            {                int c1=(sum[k]-sum[i-1]+100)%100;                int c2=(sum[j]-sum[k]+100)%100;                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+c1*c2);            }            //printf("%d %d %d\n",i,j,dp[i][j]);        }    }    printf("%d\n",dp[1][n]);}int main(void){    #ifdef ex    freopen ("../in.txt","r",stdin);    //freopen ("../out.txt","w",stdout);    #endif    int T;    scanf("%d",&T);    while (T--)    {        scanf("%d",&n);        for (int i=1;i<=n;++i) scanf("%d",&A[i]);        solve();    }}
View Code

(对于区间DP,循环的写法相比记忆化要快若干倍,但是也更容易写错,包括初始化,边界条件等都需要注意)

 

4.You Are the One (难)

原题地址:http://acm.split.hdu.edu.cn/showproblem.php?pid=4283

题意:略

题解:通过枚举区间左端点的出栈时间实现了区间的分割

详细的解题报告

 

5.Coloring Brackets

原题地址:http://www.codeforces.com/contest/149/problem/D

题意:略

题解:略

详细的解题报告

 

6.柱爷的恋爱

原题地址:http://acm.uestc.edu.cn/#/problem/show/1321

题意:略

题解:枚举左端点的配对括号来实现区间的分割。

详细的解题报告

 

总结:

区间DP主要涉及两类问题,区间最优解和区间计数。

区间最优解往往要枚举区间的分界点,分割区间,子区间的最优解合并之后就是原区间的最优解。

区间计数往往也要分割区间,但是有不遗漏不重复的要求,以上的两道题都用到了枚举左端点对应元素的技巧。

区间的分割是区间DP的重要思想

 

区间DP总结