区间dp专题

 HDU4283You Are the One区间dp,

记忆话搜索运行时间:

 
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 105,inf = 1<<30;
int dp[maxn][maxn][maxn],arr[maxn],n;
int solve(int s,int e,int k){
    if( s == e)return arr[s] * (k-1);
    if( s > e)return 0;
    if(dp[s][e][k] != inf)return dp[s][e][k];

    for(int i = s; i <= e; i++){    //将区间[s,e]划分为[s,i],[i+1,e]
        int nextk = k+(i-s)+1;      //[i+1,e]区间内第i个人第nextk个出栈
        int thisk = k+(i-s);        //[s,i]区间的第s个人第thisk个出栈
        int first = solve(s+1,i,k); //第1个区间经过分配得到最少屌丝值
        int second = solve(i+1,e,nextk); //第2个区间经过分配得到的最少屌丝值
        int cur = arr[s] * (thisk-1);     //第s个人第thisk出栈增加的屌丝值
        dp[s][e][k] = min(dp[s][e][k],cur+second+first);
    }
    return dp[s][e][k];
}
int main(){
    int t,cas = 1;
    scanf("%d",&t);
    while( t-- ){
        scanf("%d",&n);
        for(int i = 1; i <= n; i++)
            scanf("%d",arr+i);
        for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
        for(int k = 0; k <= n; k++)
        dp[i][j][k] = inf;
        printf("Case #%d: %d\n",cas++,solve(1,n,1));

    }
    return 0;
}
三个for循环
 
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn =105;
const int inf = 1<<30;
int dp[maxn][maxn],arr[maxn],sum[maxn];
int n;
int solve_dp(){
    for(int len = 1; len < n; len++){           //枚举区间长度
        for(int s = 1; s + len <= n; s ++){     //枚举区间起点
            int end = s+len;                    //s为区间头,end为区间尾
            for(int k = s; k <= end; k++){      //第k个出栈
                int tp = arr[s] * (k-s);
                tp += dp[s+1][k] + dp[k+1][end];
                tp += (k-s+1) * (sum[end] - sum[k]);//重要的性质,如果第i个人第k个出栈,那么后面的人出栈顺序都大于k
                dp[s][end] = min(dp[s][end],tp);
            }
        }
    }
    return dp[1][n];
}
int main(){
    int t,cas = 1;
    scanf("%d",&t);
    while( t-- ){
        scanf("%d",&n);
        for(int i = 1; i <= n; i++){
            scanf("%d",arr+i);
            sum[i] = sum[i-1] + arr[i];
        }
        memset(dp,0,sizeof(dp));
        for(int i = 1; i <= n; i++)
        for(int j = i+1; j <= n; j++)
        dp[i][j] = inf;
        printf("Case #%d: %d\n",cas++,solve_dp());
    }
    return 0;
}

 3、poj 2955 Brackets

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

     状态转移方程:if(mach(j,j+i))dp[j][j+i] = dp[j+1][j+i-1]+2;
                            dp[j][j+i] = max(dp[j][k],dp[k+1][j+i])(j <= k < j+i)

/*
** 一种自底向上型的DP
** 状态转移方程:if(mach(j,j+i))dp[j][j+i] = dp[j+1][j+i-1]+2;
**                dp[j][j+i] = max(dp[j][k],dp[k+1][j+i])(j <= k < j+i)
*/
#include <stdio.h>
#include <cstring>
#define Max(a,b) (a)>(b)?(a):(b)
#define maxn 101
int dp[maxn][maxn];
char str[maxn];
int main()
{
     while(~scanf("%s",str)) {
       if(strcmp(str,"end") == 0)break;
          int len = strlen(str);
          memset(dp,0,sizeof(dp));
          for(int i = 1; i <= len; i++) {//枚举区间长度
               for(int j = 0; j <= len - i; j++) {//枚举起点
                    dp[j][j+i] = dp[j+1][j+i-1] ;
                    if((str[j] == '(' && ')'== str[j+i]) || (str[j] =='[' && str[j+i] == ']' ))
                         dp[j][j+i] += 2;
                    for(int k = j; k < i+j; k++)//枚举区间j->j+i的分界点
                         dp[j][j+i] = Max(dp[j][j+i],dp[j][k]+dp[k+1][j+i]);
               }
          }
          printf("%d\n",dp[0][len-1]);
     }
     return 0;
}

 Light OJ 1422 Halloween Costumes

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

   状态转移方程:

     dp[s][e] = dp[s][e-1] (arr[s] == arr[e]).dp[s][e] = dp[s][e-1]+1 (arr[s] != arr[e]) ;
     dp[s][e] = min(dp[s][e] ,dp[s][k]+dp[k+1][e]);(arr[s] == arr[k] && s <= k < e )
     dp[s][e] = min(dp[s][e] ,dp[s][k]+dp[k+1][e]+1);(arr[s] != arr[k] && s <= k < e)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 110;
int dp[maxn][maxn],arr[maxn];
int main()
{
     int t,cas = 1,n;
     scanf("%d",&t);
     while( t-- ) {
          scanf("%d",&n);
          for(int i = 1; i <= n; i++)
               scanf("%d",&arr[i]);
          memset(dp,0,sizeof(dp));
          for(int i = 1; i <= n; i++)dp[i][i] = 1;
          for(int i = 1; i <= n; i++)
               for(int j = 1; j <= n-i; j++) {
                    dp[j][j+i] = dp[j][j+i-1];
                    if(arr[j] != arr[j+i])dp[j][j+i]++;//当起点和终点不同时衣服数量加1
                    for(int k = j; k < j+i; k++) {
                         if(arr[j] == arr[k])
                              dp[j][j+i] = min(dp[j][j+i],dp[j][k]+dp[k+1][j+i]);//当第j和第k相等时,衣服数量等于(j->k) + (k+1->j+i)
                         else
                              dp[j][j+i] = min(dp[j][j+i],dp[j][k]+dp[k+1][j+i]+1);//当j和k不相等时,衣服数量等于(j->k) + (k+1->j+i)  + 1
                    }
               }
          printf("Case %d: %d\n",cas++,dp[1][n]);
     }
     return 0;
}

 

posted @ 2013-08-12 09:53  一生挚爱  阅读(239)  评论(0编辑  收藏  举报