区间上的动态规划

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

题目:

一:石子合并(一)
时间限制: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;
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
动态规划是一种常用的算法思想,它通常用于解决一些最优化问题。在动态规划中,我们通常需要定义状态和状态转移方程。对于区间类型的动态规划问题,我们可以使用 interval 这个 Python 库来方便地进行区间操作。 下面是一个使用 interval 库进行区间类型动态规划的例子: 假设我们有一个长度为 n 的数组 nums,我们需要找到一个区间 [i,j],使得这个区间内的元素和最大。我们可以定义状态 dp[i] 表示以第 i 个元素结尾的最大子区间和,状态转移方程为 dp[i] = max(dp[i-1]+nums[i], nums[i])。最终的答案即为 max(dp)。 ```python from interval import Interval def maxSubArray(nums): n = len(nums) dp = [0] * n dp[0] = nums[0] for i in range(1, n): dp[i] = max(dp[i-1]+nums[i], nums[i]) return max(dp) nums = [-2,1,-3,4,-1,2,1,-5,4] intervals = [Interval(i, i+1) for i in range(len(nums))] max_sum = maxSubArray(nums) max_interval = max(intervals, key=lambda x: sum(nums[x.lower:x.upper])) print("最大子区间和为:", max_sum) print("最大子区间为:", max_interval) ``` 上述代码中,我们首先定义了一个长度为 n 的数组 nums,然后使用 interval 库创建了一个区间列表 intervals,其中每个区间的左端点和右端点都是相邻的两个整数。接着,我们定义了一个函数 maxSubArray,用于计算最大子区间和。在函数中,我们使用动态规划的思想,定义了状态 dp[i] 表示以第 i 个元素结尾的最大子区间和,然后使用状态转移方程 dp[i] = max(dp[i-1]+nums[i], nums[i]) 计算出所有状态的值。最后,我们返回 dp 中的最大值作为最大子区间和。 在计算出最大子区间和之后,我们使用 max 函数和 lambda 表达式找到了一个使得区间内元素和最大的区间,并将其赋值给了变量 max_interval。最终,我们输出了最大子区间和和最大子区间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值