区间dp

来源:互联网 发布:淘宝网系统架构分析 编辑:程序博客网 时间:2024/05/21 17:07


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

#include<iostream>  #include<math.h>  #include<stdio.h>  #include<algorithm>  #include<string.h>  #include<vector>  #include<queue>  #include<map>  #include<set>  #include<stack>  #define B(x) (1<<(x))  using namespace std;  typedef long long ll;  void cmax(int &a,int b){ if(b>a)a=b; }  void cmin(int &a,int b){ if(b<a)a=b; }  void cmax(ll &a,ll b){ if(b>a)a=b; }  void cmin(ll &a,ll b){ if(b<a)a=b; }  void add(int &a,int b,int mod){ a=(a+b)%mod; }  void add(ll &a,ll b,ll mod){ a=(a+b)%mod; }  void add(int &a,int b){ a+=b; }  void add(ll &a,ll b){ a+=b; }  const int oo=0x3f3f3f3f;  const ll MOD=1000000007;  const int maxn=1005;  int dp[maxn][maxn][2];  int D[maxn],W[maxn];    int main(){      int n,v,sum;      while(scanf("%d%d",&n,&v)!=EOF){          sum=0;          for(int i=1;i<=n;i++){              scanf("%d%d",&D[i],&W[i]);              sum+=W[i];              W[i]+=W[i-1];          }          memset(dp,0x3f,sizeof dp);          dp[v][v][0]=dp[v][v][1]=0;          for(int i=v-1;i>=0;i--){              cmin(dp[i][v][0],dp[i+1][v][0]+(D[i+1]-D[i])*(sum-W[v]+W[i]));              cmin(dp[i][v][1],dp[i][v][0]+(D[v]-D[i])*(sum-W[v]+W[i-1]));          }          for(int j=v+1;j<=n;j++){                cmin(dp[v][j][1],dp[v][j-1][1]+(D[j]-D[j-1])*(sum-W[j-1]+W[v-1]));              cmin(dp[v][j][0],dp[v][j][1]+(D[j]-D[v])*(sum-W[j]+W[v-1]));          }          for(int i=v-1;i>=1;i--){              for(int j=v+1;j<=n;j++){                  dp[i][j][0]=min(                  dp[i+1][j][0]+(D[i+1]-D[i])*(sum-W[j]+W[i]),                  dp[i+1][j][1]+(D[j]-D[i])*(sum-W[j]+W[i])                  );                  dp[i][j][1]=min(                  dp[i][j-1][1]+(D[j]-D[j-1])*(sum-W[j-1]+W[i-1]),                  dp[i][j-1][0]+(D[j]-D[i])*(sum-W[j-1]+W[i-1])                  );              }          }          printf("%d\n",min(dp[1][n][0],dp[1][n][1]));      }      return 0;  }  

     区间DP是一类在区间上进行动态规划的最优问题,一般是根据问题设出一个表示状态的dp,可以是二维的也可以是三维的,一般情况下为二维。然后将问题划分成两个子问题,也就是一段区间分成左右两个区间,然后将左右两个区间合并到整个区间,或者说局部最优解合并为全局最优解,然后得解。

    这类DP可以用常规的for循环来写,也可以用记忆化搜索来写,个人更倾向于记忆化搜索写法,因为这种写法相当易懂,要什么值你直接去记忆化搜索一下就ok了,随叫随到随用啊。


    1、Zoj 3537 Cake

    比较经典的三角剖分。

    状态转移方程:dp[i][j] = min(dp[i][k]+dp[k][j]+cost[i][k]+cost[k][j]),(j >= i+ 3,i+1<=k<=j-1,cost[i][k]为连一条i到k的线的费用)  

    详细报告见Here


    2、Light oj 1422 Halloween Costumes

    很基础的区间DP,是不老传说那题的减弱版。

    状态转移方程:dp[i][j] = dp[i][j-1] (arr[i] == arr[j])

                            dp[i][j] = min(dp[i][k]+dp[k+1][j]) (arr[i] == arr[k] && i <= k <= j)


     3、poj 2955 Brackets

     经典的区间DP模型--最大括号匹配数。如果找到一对匹配的括号[xxx]oooo,就把区间分成两部分,一部分是xxx,一部分是ooo,然后以此递归直到区间长度为1或者为2.

     状态转移方程:dp[i][j] = min(dp[i+1][j],dp[i+1][k-1]+dp[k+1][j]+1)(i<=k<=j&&i和k是一对括号)


     4、CF 149 D Coloring Brackets

     限制很多的括号匹配类区间DP,因为给定的是一个合法序列,那么左括号对应着右括号的位置就定下来了,那么[i,j]区间就分成了[i+1,match[i]-1]和[match[i]+1,j],再记录下某个区间左右两边的颜色就好了。 


    5、1651 Multiplication Puzzle

     经典的区间DP模型--矩阵链乘的变换。我们从一开始一步步往后删很困难进行DP,那么从最后只剩两个也就是最左和最右时开始一步步增加就成功转换为经典的矩阵链乘了。一开始的区间是[i,j],那么我们可以选择i<k<j进行转移,就是用这个k将区间划分成两段,然后这两段再往下递归,最后返回信息,最后的最后返回的[i,j]的最少乘积和。

     状态转移:dp[i][j] = min(dp[i][k]+dp[k][j]+arr[i]*arr[k]*arr[j]);


     6、Zoj 3469 Food Delivery

    有n个人叫餐,每个人都在x轴上,并且每个人都有个坑爹度(和等餐时间有关,据说顾客认为坑爹值到一定程度他的小宇宙就要爆发).现在送餐员从x轴上的某点出发,路上奔跑速度是v,要一次性把所有餐送完。叫餐的人得到餐的时间和顺序不同,坑爹度总和也就不同。合格的送餐员要让客户体验最好,请问最少坑爹度和为多少。先YY出一个结论,最优的送餐顺序肯定是餐馆往两边来回跑,然后就可以进行状态转移了.

     详细报告见Here.   


    7、Hdu 4283 You Are the One  (较难)

    题意是给定一个序列,序列内的人有屌丝值Di,是第然后将这个序列进栈,第i个人如果是第k个出栈,那么最后的屌丝总值增加Di * (k-1), 求一个出栈序列使得总屌丝值最小。看这题目很难想到区间DP吧?是的,很不像,这样的题目是好题目。其实一个合法的出栈序列有一个性质:如果第i个人第k个出栈,那么在i个之后的人不可能会比i更早,这样就找到一个划分关系,k以前位一段,k以后为一段。常规的状态转移方程:dp[i][j] = min(dp[i+1][k]+dp[k+1][j]+arr[i]*(k-i)+(k-i+1)*(sum[j]-sum[i])) (i<=k<=j).详细报告见Here.


     8、Sdut 不老的传说问题 (较难)

      刚看到题目的时候一点想法都没有,关于颜色的状态怎么表示,莫非要开一维来表示颜色,可是这样转移就很怪异了,或者倒着推,看刷几下全变成0,这似乎也不行。一筹莫展额时候突然想到根本不需要表示颜色,只需要表示某个位置是不是刷到了相应的颜色即可.5 4 3 1 2 1 2 1 3 4 5,假设5 4 3和3 4 5都刷到位了,那么1 2 1 2 1这四个位置上可能已经有其他颜色,但在我们看来它和无色一样,因为它不是最终的颜色。单独考虑1 2 1 2 1,这要怎么刷呢,由于最后的那个1和一开始那个1一样,如果他们之间距离小等于k,那么我们就可以只计算1 2 1 2。如果距离大于k,那么我们可以把这段区间分成1,2 1 2 1,或者1 2 1 , 2 1这两种方案中刷得次数较小的就是1 2 1 2 1需要刷得次数.以此递归下去,便可得解。因为本题是个圈,所以需要用到一个技巧,那就是把1.2...n-1复制到n+1,...2 * n - 1,然后最终的答案就是min(dp[i][i+n-1])(1<=i<=n)

     状态转移方程:dp[i][j] = dp[i][j-1] ; (i + k - 1 >=j && arr[j] == arr[i])

                           dp[i][j] = min(dp[i][k]+dp[k+1][j]) (1<=k<=min(i+k-1,j) && arr[i] == arr[k])

     初始化dp[i][i] = 1,dp[i][j] = INF (j > i)


     9、Hdu String painter

     2008年长春区域赛的题目,和上一题很像,但觉得比上一题简单。由于每次可以选择将某段刷成某个字符a,然后下次刷得时候,假设刷b,可以把这段刷成不一样的两段,前一段时b,后一段是a。大概的刷法就是这样,这个b其实是s2[i]。 

     状态转移方程很容易想到:dp[i][j][pre] = min(dp[i+1][k][s2[i]] + dp[k+1][pre]) (pre == s2[i] && i <= k <= j)

                                            dp[i][j][pre] = min(dp[i+1][k][s2[i]] + dp[k+1][pre] + 1) (pre != s2[i] && i <= k <= j)



     这类DP的写法一般是固定,两种写法可以参照下这篇文章里的代码:http://blog.csdn.net/woshi250hua/article/details/7973824


0 0
原创粉丝点击