区间DP

   区间DP,每段区间的最优值都由几段更小区间的最优值得到。主要方法是先求小区间的DP,再求大区间的DP。大多数情况是三重循环。大概形式是下面这样,i是区间大小,j是区间起始位置,k从0到i找出dp[i][j+i]最优解。

for(i=1;i<N;i++)

{

   for(j=0;i+j<N;j++)

   {

        if(..) dp[i][j+i]=...

        for(k=0;k<i;k++)

        dp[i][j+i]=...max(min)(dp[j][j+i],dp[j][j+k]+dp[j+k+1][j+i])

   }

}

P - Brackets
Time Limit:1000MS    Memory Limit:65536KB    64bit IO Format:%I64d & %I64u
SubmitStatus

Description

We give the following inductive definition of a “regular brackets” sequence:

  • the empty sequence is a regular brackets sequence,
  • ifs is a regular brackets sequence, then (s) and [s] are regular brackets sequences, and
  • ifa 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 charactersa1a2an, your goal is to find the length of the longest regular brackets sequence that is a subsequence ofs. That is, you wish to find the largest m such that for indicesi1, i2, …, im where 1 ≤i1 < i2 < … < imn, ai1ai2aim 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[j][j+i]=dp[j+1][j+i-1]+1;
之后就不用k循环了,事实上还是要的,比如 ()()() 这种情况。

代码:

#include<stdio.h>
#include<string.h>
char a[110];
int dp[110][110];
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    while(scanf("%s",a),strcmp(a,"end"))
    {
        int i,j,k;
        memset(dp,0,sizeof(dp));
        for(i=1; i<strlen(a); i++)
            for(j=0; j+i<strlen(a); j++)
            {
                if(a[j+i]==')'&&a[j]=='('||a[j+i]==']'&&a[j]=='[') dp[j][j+i]=dp[j+1][j+i-1]+1;
                for(k=0; k<i; k++) dp[j][i+j]=max(dp[j][i+j],dp[j][j+k]+dp[j+k+1][j+i]);
            }
        printf("%d\n",dp[0][strlen(a)-1]*2);
    }
    return 0;
}

Q - Multiplication Puzzle
Time Limit:1000MS    Memory Limit:65536KB    64bit IO Format:%I64d & %I64u
SubmitStatus

Description

The multiplication puzzle is played with a row of cards, each containing a single positive integer. During the move player takes one card out of the row and scores the number of points equal to the product of the number on the card taken and the numbers on the cards on the left and on the right of it. It is not allowed to take out the first and the last card in the row. After the final move, only two cards are left in the row.

The goal is to take cards in such order as to minimize the total number of scored points.

For example, if cards in the row contain numbers 10 1 50 20 5, player might take a card with 1, then 20 and 50, scoring
10*1*50 + 50*20*5 + 10*50*5 = 500+5000+2500 = 8000
If he would take the cards in the opposite order, i.e. 50, then 20, then 1, the score would be
1*50*20 + 1*20*5 + 10*1*5 = 1000+100+50 = 1150.

Input

The first line of the input contains the number of cards N (3 <= N <= 100). The second line contains N integers in the range from 1 to 100, separated by spaces.

Output

Output must contain a single integer - the minimal score.

Sample Input

6
10 1 50 50 20 5


这道题一开始不会做,其实是假设第k个是最后一个取,于是可以得到

dp[j][j+i]=min(dp[j][j+i],dp[j][j+k]+dp[j+k][j+i]+a[j]*a[j+k]*a[j+i])

代码:

#include<stdio.h>
#include<string.h>
#define INF 0x3f3f3f3f
int a[110];
int dp[110][110];
int min(int a,int b)
{
    return a<b?a:b;
}
int main()
{
    int N;
    while(scanf("%d",&N)!=EOF)
    {
        int i,j,k;
        memset(dp,INF,sizeof(dp));
        for(i=0; i<N; i++) scanf("%d",&a[i]);
        for(i=1; i<N; i++)
            for(j=0; j+i<N; j++)
            {
                if(i==1) dp[j][j+1]=0;
                else if(i==2) dp[j][j+i]=a[j]*a[j+1]*a[j+2];
                else for(k=1; k<i; k++) dp[j][j+i]=min(dp[j][j+i],dp[j][j+k]+dp[j+k][j+i]+a[j]*a[j+k]*a[j+i]);
            }
        printf("%d\n",dp[0][N-1]);
    }
    return 0;
}


R - You Are the One
Time Limit:1000MS    Memory Limit:32768KB    64bit IO Format:%I64d & %I64u
SubmitStatus

Description

  The TV shows such as You Are the One has been very popular. In order to meet the need of boys who are still single, TJUT hold the show itself. The show is hold in the Small hall, so it attract a lot of boys and girls. Now there are n boys enrolling in. At the beginning, the n boys stand in a row and go to the stage one by one. However, the director suddenly knows that very boy has a value of diaosi D, if the boy is k-th one go to the stage, the unhappiness of him will be (k-1)*D, because he has to wait for (k-1) people. Luckily, there is a dark room in the Small hall, so the director can put the boy into the dark room temporarily and let the boys behind his go to stage before him. For the dark room is very narrow, the boy who first get into dark room has to leave last. The director wants to change the order of boys by the dark room, so the summary of unhappiness will be least. Can you help him?
 

Input

  The first line contains a single integer T, the number of test cases.  For each case, the first line is n (0 < n <= 100)
  The next n line are n integer D1-Dn means the value of diaosi of boys (0 <= Di <= 100)
 

Output

  For each test case, output the least summary of unhappiness .
 

Sample Input

     
     
     
     
2    5 1 2 3 4 5 5 5 4 3 2 2
 

Sample Output

   
   
   
   
Case #1: 20 Case #2: 24


题目意思是说可以调换顺序,但是先进的要后出,所以DP就应该考虑把队首元素换到后面的哪个地方k,在换到位置之前的区间DP加上它的值乘以k再加上后面的DP*(k+1),(因为后面的每个元素都往后移动了k+1个位置)这样找到最优解。不知道为什么我开始总想着从后往前,那样要满足先进后出的话整个队列的顺序都要反过来=。=不知道是怎么想的。。

代码:


#include<stdio.h>
#include<string.h>
#define INF 0x3f3f3f3f
int a[110],c[110];
int dp[110][110];
int min(int a,int b)
{
    return a<b?a:b;
}
int main()
{
    int N,T,Case=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&N);
        int i,j,k;
        c[0]=0;
        memset(dp,INF,sizeof(dp));
        for(i=0; i<N; i++)
        {
            scanf("%d",&a[i]);
            c[i]=(i==0?a[i]:c[i-1]+a[i]);
            dp[i][i]=dp[i+1][i]=0;
        }
        for(i=1; i<N; i++)
            for(j=0; i+j<N; j++)
            {
                dp[j][j+i]=min(dp[j][j+i],dp[j+1][j+i]+c[j+i]-c[j]);
                for(k=1; k<=i; k++)
                {
                    dp[j][j+i]=min(dp[j][j+i],dp[j+1][j+k]+a[j]*k+dp[j+k+1][j+i]+(c[j+i]-c[j+k])*(k+1));
                }
            }
        printf("Case #%d: %d\n",++Case,dp[0][N-1]);
    }
    return 0;
}

T - String painter
Time Limit:2000MS    Memory Limit:32768KB    64bit IO Format:%I64d & %I64u
SubmitStatus

Description

There are two strings A and B with equal length. Both strings are made up of lower case letters. Now you have a powerful string painter. With the help of the painter, you can change a segment of characters of a string to any other character you want. That is, after using the painter, the segment is made up of only one kind of character. Now your task is to change A to B using string painter. What’s the minimum number of operations?
 

Input

Input contains multiple cases. Each case consists of two lines:
The first line contains string A.
The second line contains string B.
The length of both strings will not be greater than 100.
 

Output

A single line contains one integer representing the answer.
 

Sample Input

     
     
     
     
zzzzzfzzzzz abcdefedcba abababababab cdcdcdcdcdcd
 

Sample Output

   
   
   
   
6 7


问的是可以把A的任意区间改成别的字母,最少改几次变成B。直接变不好弄,可以先算出空白串变成B最少要几次,再算A到B。dp[i][j]包括位置i和j,所以i==j的话dp是1。首先dp[i][j]=min(dp[i-1][j],dp[i][j-1]),我一开始写的是dp[i][j]=dp[i][j-1]+1,结果WA。然后如果dp[i+j]==dp[i+k],就有dp[j][j+i]=min(dp[j][j+i],dp[j][j+k-1]+dp[j+k][j+i-1])。空白串变B处理完了,接着就是A变B,又是区间DP。。用ans[i]存到i-1个位置的答案,于是先把ans[i]赋值dp[0][i],再尝试减小ans[i]。最后ans[N-1]就是答案。

 for(i=0; i<N; i++)
    {
        ans[i]=dp[0][i];
        if(a[i]==b[i]) ans[i]=i==0?0:ans[i-1];
        for(j=0; j<i; j++) ans[i]=min(ans[i],ans[j]+dp[j+1][i]);
    }

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#define INF 0x3f3f3f3f
char a[110],b[110];
int dp[110][110];
int ans[110];
int min(int a,int b)
{
    return a<b?a:b;
}
int main()
{
    while(scanf("%s",a)!=EOF)
    {
        scanf("%s",b);
        int i,j,k,N=strlen(b);
        for(i=0; i<N; i++)
            for(j=0; j<=i; j++)
            {
                if(i==j)dp[i][j]=1;
                else dp[i][j]==0;
            }
        for(i=1; i<N; i++)
            for(j=0; j+i<N; j++)
            {
                dp[j][j+i]=min(dp[j][j+i-1],dp[j+1][j+i])+1;
                for(k=0; k<i; k++) if(b[j+k]==b[j+i])
                        dp[j][j+i]=min(dp[j][j+i],dp[j][j+k-1]+dp[j+k][j+i-1]);
            }
        for(i=0; i<N; i++)
        {
            ans[i]=dp[0][i];
            if(a[i]==b[i]) ans[i]=i==0?0:ans[i-1];
            for(j=0; j<i; j++) ans[i]=min(ans[i],ans[j]+dp[j+1][i]);
        }
        printf("%d\n",ans[N-1]);
    }
    return 0;
}


  额。。其实有些DP很难想到,还是要多做才有经验。。。

你可能感兴趣的:(区间DP)