区间DP 入门经典三道题

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

#include <cstdio>
#include <iostream>
#include <cstring>
#define sf scanf
#define pf printf

using namespace std;
typedef long long LL;
const int maxn = 205;
/*
    题意: 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))}

*/
const LL INF = 0x3f3f3f3f;
LL DP[maxn][maxn];
LL A[maxn];
LL DPS(int l,int r){
    if(DP[l][r] != -1) return DP[l][r];
    LL& ans = DP[l][r];
    ans = INF;
    LL L = 0,R = 0;
    for(int i = l;i <= r;++i) R += A[i];
    for(int k = l;k < r;++k){
        L += A[k],R -= A[k];
        ans = min(ans,L + R + DPS(l,k) + DPS(k + 1,r));
    }
    return ans;
}
int main(){
    int n;
    while( ~sf("%d",&n) ){
        for(int i = 0;i < n;++i) sf("%lld",A + i);
        memset(DP,-1,sizeof(DP));
        for(int i = 0;i < n;++i) DP[i][i] = 0;
        DPS(0,n - 1);
        pf("%lld\n",DP[0][n - 1]);
    }
    return 0;
}

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

#include <cstdio>
#include <iostream>
#include <cstring>
#define sf scanf
#define pf printf
using namespace std;
/*
    题意:给一个只包含‘[’,‘]’,‘(’,‘)’的字符串求在这个字符串的一个最长的子串 是一个合法括号字符
    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]);
*/
const int INF = 0x3f3f3f3f;
const int maxn = 105;
int DP[maxn][maxn];
char s[maxn];
int DPS(int l,int r){
    if(~DP[l][r]) return DP[l][r];
    int& ans = DP[l][r];
    ans = INF;
    if(s[l] == '(' && s[r] == ')' || s[l] == '[' && s[r] == ']') ans = min(ans,DPS(l + 1,r - 1));
    for(int k = l;k < r;++k){
        ans = min(ans,DPS(l,k) + DPS(k + 1,r));
    }
    return ans;
}


int main(){
    while(sf("%s",s)){
        if(s[0] == 'e') break;
        int len = strlen(s);
        memset(DP,-1,sizeof(DP));
        for(int i = 0;i < len;++i){
            DP[i][i] = 1;
            DP[i + 1][i] = 0;
        }
        pf("%d\n",len - DPS(0,len - 1));
    }
    return 0;
}

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

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#define sf scanf
#define pf printf
using namespace std;
typedef long long int LL;
/*  分类 区间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)

*/

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


int main(){
    int T;sf("%d",&T);
    while( T-- ){
        memset(DP,-1,sizeof(DP));
        int n,m;
        sf("%s %d",s,&m);
        len = n = strlen(s);
        F[len] = 1;
        G[len] = 0;
        for(int i = len - 1;~i;--i) {
            F[i] = F[i + 1] * 10;
            G[i] = G[i + 1] + (s[i] - '0') * F[i + 1];
        }
//        pf("%lld\n",G[0] / F[1]);
        pf("%lld\n",DPS(0,n - 1,m));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值