区间上的动态规划

区间型动态规划是线性动态规划的拓展,它以区间长度为阶段,长区间的取值依赖于短区间的最优值。

题目:

一:石子合并(一)
时间限制:1000 ms | 内存限制:65535 KB
难度:3
描述
有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。
输入
有多组测试数据,输入到文件结束。
每组测试数据第一行有一个整数n,表示有n堆石子。
接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开
输出
输出总代价的最小值,占单独的一行
样例输入
3
1 2 3
7
13 7 8 16 21 4 18
样例输出
9
239

题目分析:
首先我们需要先定义状态:dp[i][j]:区间[i,j]的合并石子的总的代价的最小值。
下面我们就考虑它的转移方程或者说最优子结构了:
我们可以把dp[i][j]分成两个子区间:[i,k],[k+1,j],那么就可以知道它的转移方程了:
dp[i][j]=min(dp[i][k]+dp[k+1][j]+sum(i,j)),(i < k < j)
当然,还有一个很重要的东西就是初始状态:dp[i][i]=0,即如果只有一堆石子就不用移动了。
注意从小区间推大区间
Accepted code:


#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn=205;
int stone[maxn];
int dp[maxn][maxn];
int n;
int sum[maxn];

int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;i++)
        {
            scanf("%d",&stone[i]);
        }
        sum[0]=0;
        for(int i=1;i<=n;i++) sum[i]=sum[i-1]+stone[i-1];

        //len=1
        for(int i=0;i<n;i++) dp[i][i]=0;
        //len=2
        for(int i=0;i<n-1;i++) dp[i][i+1]=stone[i]+stone[i+1];
        //len>=3
        for(int l=3;l<=n;l++)
        {
            for(int s=0;s+l-1<n;s++)
            {
                int t=s+l-1;
                int temp=0x3f3f3f3f;
                for(int k=s;k<t;k++)
                {
                    temp=min(temp,dp[s][k]+dp[k+1][t]+sum[t+1]-sum[s]);
                }
                dp[s][t]=temp;
            }
        }
        printf("%d\n",dp[0][n-1]);
    }
}

二、括号匹配(二)
时间限制:1000 ms | 内存限制:65535 KB
难度:6
描述
给你一个字符串,里面只包含”(“,”)”,”[“,”]”四种符号,请问你需要至少添加多少个括号才能使这些括号匹配起来。
如:
[]是匹配的
([])[]是匹配的
((]是不匹配的
([)]是不匹配的
输入
第一行输入一个正整数N,表示测试数据组数(N<=10)
每组测试数据都只有一行,是一个字符串S,S中只包含以上所说的四种字符,S的长度不超过100
输出
对于每组测试数据都输出一个正整数,表示最少需要添加的括号的数量。每组测试输出占一行
样例输入
4
[]
([])[]
((]
([)]
样例输出
0
0
3
2
来源
《算法艺术与信息学竞赛》
上传者
张云聪

分析:
这是一题比较典型的区间dp题。首先我们需要先定义状态:dp[i][j]:表示在区间[i,j]需要添加的最少括号个数。根据不同结构,可以分为以下 4 种不同情况来处理:
1)S 形如(S′)或[S′]:
只需把 S′变规则即可,则 f[i,j]= f[i+1,j-1]。
2)S 形如(S′或[S′: 先把 S′化为规则的,右边加“)”或“]”即可,则 f[i,j]= f[i+1,j]+1。
3)S 形如 S′)或 S′]: 先把 S′化为规则的,左边加“(”或“[”即可,则 f[i,j]= f[i,j-1]+1
4)把长度大于 1 的序列 SiSi+1..Sj-1Sj分为两部分: Si..Sk,Sk+1.. Sj,分别化为规则序列,则 f[i,j]=f[i,k]+f[k+1,j] ;i<=k<=j-1; 上述 4 种情况取最小值即可。

特殊数据
()() 0
(我就是卡上面那个数据卡了很久)

Accepted code:



#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=205;
int dp[maxn][maxn];
char s[maxn];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",s);
        int n=(int)strlen(s);
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++) dp[i][i]=1;

        for(int i=n;i>=0;i--)
        {
            for(int j=i;j<n;j++)
            {
                int temp=0x3f3f3f3f;
                if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']'))
                {
                    temp=dp[i+1][j-1];
                }

                for(int k=i;k<j;k++)
                {
                    temp=min(temp,dp[i][k]+dp[k+1][j]);
                }
                int v1=0x3f3f3f3f,v2=0x3f3f3f3f;
                if(s[j]==')'||s[j]==']')
                    v1=dp[i][j-1]+1;
                if(s[i]=='('||s[i]=='[')
                    v2=dp[i+1][j]+1;
                dp[i][j]=min(v1,min(v2,temp));

            }
        }
        printf("%d\n",dp[0][n-1]);
    }
}



三、 Halloween Costumes LightOJ - 1422
Gappu has a very busy weekend ahead of him. Because, next weekend is Halloween, and he is planning to attend as many parties as he can. Since it’s Halloween, these parties are all costume parties, Gappu always selects his costumes in such a way that it blends with his friends, that is, when he is attending the party, arranged by his comic-book-fan friends, he will go with the costume of Superman, but when the party is arranged contest-buddies, he would go with the costume of ‘Chinese Postman’.

Since he is going to attend a number of parties on the Halloween night, and wear costumes accordingly, he will be changing his costumes a number of times. So, to make things a little easier, he may put on costumes one over another (that is he may wear the uniform for the postman, over the superman costume). Before each party he can take off some of the costumes, or wear a new one. That is, if he is wearing the Postman uniform over the Superman costume, and wants to go to a party in Superman costume, he can take off the Postman uniform, or he can wear a new Superman uniform. But, keep in mind that, Gappu doesn’t like to wear dresses without cleaning them first, so, after taking off the Postman uniform, he cannot use that again in the Halloween night, if he needs the Postman costume again, he will have to use a new one. He can take off any number of costumes, and if he takes off k of the costumes, that will be the last k ones (e.g. if he wears costume A before costume B, to take off A, first he has to remove B).

Given the parties and the costumes, find the minimum number of costumes Gappu will need in the Halloween night.

Input
Input starts with an integer T (≤ 200), denoting the number of test cases.

Each case starts with a line containing an integer N (1 ≤ N ≤ 100) denoting the number of parties. Next line contains N integers, where the ith integer ci (1 ≤ ci ≤ 100) denotes the costume he will be wearing in party i. He will attend party 1 first, then party 2, and so on.

Output
For each case, print the case number and the minimum number of required costumes.

Sample Input
2
4
1 2 1 2
7
1 2 1 1 3 2 1
Sample Output
Case 1: 3
Case 2: 4

分析:
题目的意思就是Gappu要去参加party,然后需要穿相应的衣服,衣服用数字表示,给你第i场party需要穿的衣服,求需要的最少衣服,注意脱下来后不能再穿回去。
定义状态dp[i][j]:从第i场party到第j场party需要穿的最少的衣服。
还是把区间分割:
dp[i][j]=dp[i][k]+dp[k+1][j],其中需要costume[j]==costume[k]
初始化:
dp[i][i]=1;

Accepted code:

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;
const int maxn=105;
int costume[maxn];
int dp[maxn][maxn];

int main()
{
    int t;
    cin>>t;
    int kase=1;
    while(t--)
    {

        int n;
        cin>>n;
        for(int i=0;i<n;i++)
            cin>>costume[i];

        memset(dp,0,sizeof(dp));

        for(int i=0;i<n;i++)
            for(int j=i;j<n;j++)
                dp[i][j]=j-i+1;

        for(int i=n-1;i>=0;i--)
        {
            for(int j=i+1;j<n;j++)
            {
                //turn off costume
                int temp=0x3f3f3f3f;
                for(int k=i;k<j;k++)
                    if(costume[k]==costume[j])
                        temp=min(temp,dp[i][k]+dp[k+1][j-1]);
                //dress up
                dp[i][j]=min(temp,dp[i][j-1]+1);
            }
        }

        cout<<"Case "<<kase++<<": "<<dp[0][n-1]<<endl;
    }
}

四:F - Brackets POJ - 2955
We give the following inductive definition of a “regular brackets” sequence:

the empty sequence is a regular brackets sequence,
if s is a regular brackets sequence, then (s) and [s] are regular brackets sequences, and
if a and b are regular brackets sequences, then ab is a regular brackets sequence.
no other sequence is a regular brackets sequence
For instance, all of the following character sequences are regular brackets sequences:

(), [], (()), ()[], ()[()]

while the following character sequences are not:

(, ], )(, ([)], ([(]

Given a brackets sequence of characters a1a2 … an, your goal is to find the length of the longest regular brackets sequence that is a subsequence of s. That is, you wish to find the largest m such that for indices i1, i2, …, im where 1 ≤ i1 < i2 < … < im ≤ n, ai1ai2 … aim is a regular brackets sequence.

Given the initial sequence ([([]])], the longest regular brackets subsequence is [([])].

Input
The input test file will contain multiple test cases. Each input test case consists of a single line containing only the characters (, ), [, and ]; each input test will have length between 1 and 100, inclusive. The end-of-file is marked by a line containing the word “end” and should not be processed.

Output
For each input case, the program should print the length of the longest possible regular brackets subsequence on a single line.

Sample Input
((()))
()()()
([]])
)[)(
([][][)
end
Sample Output
6
6
4
0
6

分析:
还是按照之前的方法,把子区间分割,然后如果匹配,则dp[i][j]=dp[i+1][j-1]+2’

AC code:

#include<iostream>
#include<cstring>
using namespace std;

const int maxn=105;
int dp[maxn][maxn];

int main()
{
    string s;
    while(cin>>s)
    {
        if(s=="end") break;
        memset(dp,0,sizeof(dp));

        for(int i=s.size()-1;i>=0;i--)
        {
            for(int j=i;j<s.size();j++)
            {
                if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']'))
                {
                    dp[i][j]=dp[i+1][j-1]+2;
                }
                for(int k=i;k<j;k++)
                {
                    dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);
                }

            }
        }
        cout<<dp[0][s.size()-1]<<endl;
    }
}

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/QingyingLiu/article/details/80687225
个人分类: DP
想对作者说点什么? 我来说一句

最大加权区间调度问题详解

2016年12月21日 105KB 下载

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭