区间dp(括号匹配末班)

O - Treats for the Cows

入门题: O - Treats for the Cows

题意:在一个长度为n的区间里,你可以在两端(左端或者右端)取出一个数,这个数乘以他是第几次取出来的。求和的最大值。
思路:一眼贪心模拟简单题。后来发现不行一旦一个数字很大就没法贪心了。给个数据

5
90
80
1
2
100

所以就想到d一下。不能常规d这样无法确定最终状态。看了下题解,顺便学习一下区间dp,具体看题解。
题解:dp[ i ][ j ]:表示在区间[ i , j ](j>=i) 里可以获得的最大值。
如果我们从里往外进行计算,找一个数以他作为最后取出来的,由题意可以知道区间[ i , j ]只可能由区间 a[ i ] + [ i+1,j ]或者 [ i , j-1 ] +a[ j ]组成,结合题意
那么状态转移是dp[ i ][ j ] = max ( dp[ i ][ j - 1 ] + a[ j ] * ( n - ( j - i ) , dp[ i + 1 ][ j ] + a[ i ] * ( n - ( j - i ) ) );
这里i要倒序否则影像记录状态(看转移方程)输入用1-n记录j-1越界麻烦。
题解参考(转载)

Brackets sequence UVA - 1626

括号匹配问题: 输入输出太毒瘤了。

**题意:**一组只由[ ,],(, ),组成的字符串,然后对他进行增添字符,使其匹配;求给他增添的最少的括号的数量,然后输出匹配后的序列;
看了一个括号匹配学习了一下自己写没过 用那个学习的源代码也没过(那个不针对题目只针对这类问题)一脸懵逼。后来发现各种输入输出毒瘤,改了好一会终于过啦。
输入:数字后面有空格,每个字符串前面有空行。一整行读入字符串。
输出:每个输出之间有空行。最后一行后面没有空行。
题解: dp[i][j]表示区间内需要匹配得数。
如果两端相同dp[i][j]=dp[i+1][j-1]没啥好解释的。
如果两端不同分为两段去找最小的数在i-j找k试得数最小
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);

还挺有意思为什么分为两段,那是因为dp本质就是枚举二分记忆化(不懂建议回到本文开始点击开始dp之路的链接。)

#include <bits/stdc++.h>
#define mem(s,ch) memset(s,ch,sizeof(s))
typedef long long LL;
#define inf 0x3f3f3f3f
const long long N=1000000;
const long long mod=1e9+7;
#define endl '\n'
using namespace std;
bool check(char a,char b){
    if((a=='('&&b==')')||(a=='['&&b==']')) return true;
    else return false;
}
int dp[110][110];
char q[110];
void print(int l,int r){
    if(l>r) return;
    if(l==r){
        if(q[l]=='('||q[l]==')') cout<<"()";
        else if(q[l]=='['||q[l]==']') cout<<"[]";
        return ;
    }
    if(check(q[l],q[r])&&dp[l][r]==dp[l+1][r-1]){
        cout<<q[l];
        print(l+1,r-1);
        cout<<q[r];
        return ;
    }
    for(int k=l;k<r;k++){
        if(dp[l][r]==dp[l][k]+dp[k+1][r]){
            print(l,k);
            print(k+1,r);
            return ;
        }
    }
}
int main(){
    int c;
    cin>>c;getchar();
    for(int ii = 1; ii <= c; ii ++)
    {
        cin.getline(q,110);
        cin.getline(q,110);
//cin>>q;
        int len=strlen(q);
        memset(dp,0,sizeof(dp));
        for(int i=0;i<len;i++) dp[i][i]=1;
        for(int i=len-2;i>=0;i--){
            for(int j=i+1;j<len;j++){
                dp[i][j]=inf;
                if(check(q[i],q[j])) dp[i][j]=dp[i+1][j-1];
                for(int k=i;k<j;k++){
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
                }
            }
        }
//        cout<<dp[0][len-1]<<endl;
        print(0,len-1);
        cout<<endl;
        if(ii < c)
            cout<<endl;
    }
    return 0;
}

同类型题(没有这题读入如此奇葩都很简单):Brackets
Brackets Sequence

Cheapest Palindrome POJ - 3280s

题目链接: Cheapest Palindrome POJ - 3280s

你需要懂得前置技能点:如何是得一个字符串添加最少的字母使其为回文串:
1.得字符串逆串求两个最长公共序列
2.区间dp
这题题意:一个由m种字母组成的长度为n的串,有各字母添加和删除花费的代价,求为回文最小代价。

题解链接:POJ-3280 Cheapest Palindrome—区间DP
代码还没完全写完,看题解链接。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值