20200419区间DP训练总结

区间DP即求解一段区间上的最优解。主要是通过合并小区间的 最优解进而得出整个大区间上最优解的dp算法,主要运用三层循环嵌套,例如

memset(dp,0,sizeof(dp))//初始dp数组
for(int len=2;len<=n;len++)//枚举区间长度
{
    for(int i=1;i<n;++i)//枚举区间的起点
    {
        int j=i+len-1;//根据起点和长度得出终点
        if(j>n) break;//符合条件的终点
        for(int k=i;k<=j;++k)//枚举最优分割点
            dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j]);//状态转移方程
    }
}         

我觉得区间DP主要是考虑大区间如何可以转化成小区间,然后找出边界条件,找出状态转移方程,继而求解。
1.Halloween Costumes
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
题目的主要意思是给你一串数字代表主人公这N个parties需要穿的衣服种类,但是每件衣服只能穿一次,脱了就不能再穿(再穿需要衣服总数加一),衣服可以套着穿,问主人公参加这N个派对至少需要几件衣服。
这个题就可以运用区间DP,用dp[i][j]表示第i-j天需要的最少衣服数,先初始化dp[i][j]=dp[i][j-1]+1,再用一个中间数k找是否有某一天 ( k ) 衣服和第 j 天的衣服一样,若一样 dp [i] [j] = min( dp [i] [j] , dp [i] [k] + dp [k+1] [j-1] ),意味着第k天到第j天一直穿着这件衣服,直到第j天再把他外边的衣服脱掉只剩这一件衣服(第k天穿的衣服)。
我第一次提交的代码WA了,主要是初始化的时候考虑的有问题,我刚开始想的是把他们全部初始称最大值再进行最小值的求解,所以写成了dp[i][j]=j-i+1,忽略了并不是计算中的所有过程都会有某个k使得c[k]==c[j],导致答案出错
AC代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int dp[120][120],t,n,c[120],i,j,k,ans;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        memset(dp,0,sizeof(dp));
        for(i=1;i<=n;i++)
            scanf("%d",&c[i]);
        for(i=1;i<=n;i++) dp[i][i]=1;
        for(j=2;j<=n;j++)
            for(i=1;i<j;i++)
            {
                dp[i][j]=dp[i][j-1]+1;
                for(k=i;k<j;k++)
                   if(c[k]==c[j])
                       dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j-1]);
            }
        printf("Case %d: %d\n",++ans,dp[1][n]);
    }
    return 0;
}

2.Brackets
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
写这个题的时候一直超时,到现在也还没搞清楚超时的原因,中途换了gets用来获取字符串,同时改cout为printf也一直超时。只能不停的换方法,下面说一下我的AC思路
dp[i][j]依旧是表示从第i-1个括号到第j-1个括号中最大匹配数
先列举区间的两端点,当这个区间两端点的括号匹配则加2,然后枚举分割点,得出每个区间的最大值即可,这样用区间DP,找到了状态转移方程,代码就可以比较轻松的写出来了

#include<iostream>
#include<cstdio>
#include<cmath>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
char a[1005];
int dp[1005][1005];
int p,q;
int main()
{
    while(scanf("%s",a))
    {
        if(a[0]=='e') break;
        n = strlen(a);
        memset(dp,0,sizeof(dp));
        for(int len=1;len<n;len++)
        {
            for(int i=0,j=len;j<n;i++,j++)
            {
                if(a[i]=='('&&a[j]==')' || a[i]=='['&&a[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]);
            }
        }
        printf("%d\n",dp[0][n-1]);
    }
    return 0;
}

在写完这个题后,我在上网看别人的解题思路时,看到了一个类似的括号匹配问题,大致题意与本题类似,不过所要考虑的问题是要使添加的括号尽量少,我们需要使原来的括号序列尽可能多得匹配,即先求最大匹配数量(跟上题一样),那么还剩下一些没有匹配的括号,我们就需要依次加上一个括号使它们得到匹配。综上所述,所求=原序列括号数量-最大匹配括号数量。(因此此题的代码与上题几乎一致)。
dp部分代码

for(int i=1;i<=n;i++)        
{            
    int j=i+len-1;           
     if(j>n) break;            
     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][j]);            
     }        
 }        
 printf("%d\n",n-dp[1][n]);

本周做的题确实是非常少,一直觉得离截止时间还远,就一直纵容自己一拖再拖,我觉得自己应该合理的安排一下时间,把任务平均分配到每一天,同时改掉爱看题解的臭毛病,把不会做但应用所学知识能够解答出来的题储存起来,没事就解一解,一定会有收获。本周打了一次cf,分升的少得可怜,本周争取打两场cf,争取能早日回到初始rating。
Fighting!

原创文章 25 获赞 38 访问量 840

猜你喜欢

转载自blog.csdn.net/weixin_46434074/article/details/105614055