区间DP

                                                        区间dp

一、题目链接

Brackets
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 4946 Accepted: 2652

Description

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 i1i2, …, imwhere 1 ≤ i1 < i2 < … < im ≤ nai1ai2 … 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

Source


题意:求匹配括号的个数。

题解:

三步骤:1.写判断满足题意的函数;2.数据初始化;3.区间dp的更新。

   关于dp更新:1.[ i,j ]的区间中,设j为新加入的点,若 i,j 匹配,则dp[i][j]=dp[i+1][j-1]+2.   除此之外,还有持续地下一步更新,把区间以k+1分为两段,设k+1与j匹配,则有dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<iostream>  
  4. #include<algorithm>  
  5. #include<cmath>  
  6. #include<queue>  
  7. #include<cstdlib>  
  8. #include<string>  
  9. using namespace std;  
  10.   
  11. #define N 110  
  12.   
  13. char str[N];  
  14. int dp[N][N];  
  15. bool check(int i,int j)  
  16. {  
  17.     if(str[i]=='['&&str[j]==']'return true;  
  18.     if(str[i]=='('&&str[j]==')'return true;  
  19.     return false;  
  20. }  
  21.   
  22. int main()  
  23. {  
  24.     while(~scanf("%s",str))  
  25.     {  
  26.         if(str[0]=='e')  
  27.             break;  
  28.         memset(dp,0,sizeof(dp));  
  29.         int l=strlen(str);  
  30.         for(int i=0;i<l-1;i++)  
  31.             if(check(i,i+1)) dp[i][i+1]=2;  
  32.         for(int n=3;n<=l;n++)  
  33.         {  
  34.             for(int i=0; i+n-1<l; i++)  
  35.             {  
  36.                 if(check(i,i+n-1)) dp[i][i+n-1]=dp[i+1][i+n-2]+2;  
  37.                 for(int j=i;j<i+n-1;j++)  
  38.                     dp[i][i+n-1]=max(dp[i][i+n-1],dp[i][j]+dp[j+1][i+n-1]);  
  39.             }  
  40.         }  
  41.         printf("%d\n",dp[0][l-1]);  
  42.     }  
  43.     return 0;  
  44. }  

二、记忆化搜索的方法。

整合在一起的代码是 d[i][j]=max(d[i+1]d[j],d[i+1][k-1]+d[k+1][j]+2);  i<k<=j; i和k匹配取后者,否之前者。

注意:不匹配的时候注意记得更新 d[i][j]=solve(i+1,j);

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<iostream>  
  4. #include<algorithm>  
  5. #include<cmath>  
  6. #include<queue>  
  7. #include<cstdlib>  
  8. #include<string>  
  9. using namespace std;  
  10.   
  11. #define N 110  
  12.   
  13. char str[N];  
  14. int dp[N][N];  
  15. bool check(int i,int j)  
  16. {  
  17.     if(str[i]=='['&&str[j]==']'return true;  
  18.     if(str[i]=='('&&str[j]==')'return true;  
  19.     return false;  
  20. }  
  21.   
  22. int solve(int i,int j)  
  23. {  
  24.     if(dp[i][j]>=0) return dp[i][j];  
  25.     if(j<=i)  return dp[i][j]=0;  
  26.     if(j==i+1)  
  27.     {  
  28.         if(check(i,j))  
  29.           return dp[i][j]=2;  
  30.         else  
  31.           return  dp[i][j]=0;  
  32.     }  
  33.     dp[i][j]=solve(i+1,j);     //不满足匹配的条件时的值。  
  34.     for(int k=i+1;k<=j;k++)  
  35.     {  
  36.         if(check(i,k))  
  37.             dp[i][j]=max(dp[i][j],solve(i+1,k-1)+solve(k+1,j)+2);  
  38.     }  
  39.      return dp[i][j];  
  40. }  
  41.   
  42. int main()  
  43. {  
  44.     while(~scanf("%s",str))  
  45.     {  
  46.         if(str[0]=='e')  
  47.             break;  
  48.         memset(dp,-1,sizeof(dp));  
  49.         int l=strlen(str);  
  50.         printf("%d\n",solve(0,l-1));  
  51.     }  
  52.     return 0;  
  53. }  



二、想看题目?戳我呀

Language:
Multiplication Puzzle
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 8012 Accepted: 4958

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

Sample Output

3650

Source

Northeastern Europe 2001, Far-Eastern Subregion

题意:问取出一段数字除首尾外的所有数字的最小消费是多少,取出每个数字的消费是 该数字与左右两端数字的乘积。


题解:运用区间dp的方法。枚举区间[i,j]中 i+1 到j-1 中的每个值,看最小消费是多少。

dp[i,j]=min(dp[i][j],dp[i][k]+dp[k][j]+num[i]*num[k]*num[j]); i<k<j;

注意初始化,动态规划中的初始化需要特别重视,初始化出现错误,结果就会错。

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<iostream>  
  4. #include<algorithm>  
  5. #include<cmath>  
  6. #include<queue>  
  7. #include<cstdlib>  
  8. #include<string>  
  9. using namespace std;  
  10.   
  11. #define N 105  
  12. #define inf 99999999  
  13.   
  14. int num[N];  
  15. int dp[N][N];  
  16.   
  17. int main()  
  18. {  
  19.     int n;  
  20.     while(~scanf("%d",&n))  
  21.     {  
  22.         for(int i=1;i<=n;i++)  
  23.             scanf("%d",&num[i]);  
  24.         for(int i=0;i<=N;i++)  
  25.             for(int j=0;j<=N;j++)  
  26.               dp[i][j]=inf;  
  27.         for(int i=1;i<n;i++)  
  28.         {  
  29.             dp[i][i+2]=num[i]*num[i+1]*num[i+2];  
  30.             dp[i][i]=dp[i][i+1]=0;  
  31.         }  
  32.   
  33.         for(int k=4;k<=n;k++)  
  34.         {  
  35.              for(int i=1;i+k-1<=n;i++)  
  36.              {  
  37.                  for(int j=i+1;j<i+k-1;j++)  
  38.                     dp[i][i+k-1]=min(dp[i][i+k-1],dp[i][j]+dp[j][i+k-1]+num[i]*num[i+k-1]*num[j]);  
  39.              }  
  40.         }  
  41.         printf("%d\n",dp[1][n]);  
  42.     }  
  43.     return 0;  
  44. }  

三、想看题目?戳我呀

Language:
Cheapest Palindrome
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 7449 Accepted: 3601

Description

Keeping track of all the cows can be a tricky task so Farmer John has installed a system to automate it. He has installed on each cow an electronic ID tag that the system will read as the cows pass by a scanner. Each ID tag's contents are currently a single string with length M (1 ≤ M ≤ 2,000) characters drawn from an alphabet of N (1 ≤ N ≤ 26) different symbols (namely, the lower-case roman alphabet).

Cows, being the mischievous creatures they are, sometimes try to spoof the system by walking backwards. While a cow whose ID is "abcba" would read the same no matter which direction the she walks, a cow with the ID "abcb" can potentially register as two different IDs ("abcb" and "bcba").

FJ would like to change the cows's ID tags so they read the same no matter which direction the cow walks by. For example, "abcb" can be changed by adding "a" at the end to form "abcba" so that the ID is palindromic (reads the same forwards and backwards). Some other ways to change the ID to be palindromic are include adding the three letters "bcb" to the begining to yield the ID "bcbabcb" or removing the letter "a" to yield the ID "bcb". One can add or remove characters at any location in the string yielding a string longer or shorter than the original string.

Unfortunately as the ID tags are electronic, each character insertion or deletion has a cost (0 ≤ cost ≤ 10,000) which varies depending on exactly which character value to be added or deleted. Given the content of a cow's ID tag and the cost of inserting or deleting each of the alphabet's characters, find the minimum cost to change the ID tag so it satisfies FJ's requirements. An empty ID tag is considered to satisfy the requirements of reading the same forward and backward. Only letters with associated costs can be added to a string.

Input

Line 1: Two space-separated integers:  N and  M 
Line 2: This line contains exactly  M characters which constitute the initial ID string 
Lines 3.. N+2: Each line contains three space-separated entities: a character of the input alphabet and two integers which are respectively the cost of adding and deleting that character.

Output

Line 1: A single line with a single integer that is the minimum cost to change the given name tag.

Sample Input

3 4
abcb
a 1000 1100
b 350 700
c 200 800

Sample Output

900

Hint

If we insert an "a" on the end to get "abcba", the cost would be 1000. If we delete the "a" on the beginning to get "bcb", the cost would be 1100. If we insert "bcb" at the begining of the string, the cost would be 350 + 200 + 350 = 900, which is the minimum.

Source


题意:问 让给出的的字符串变为回文串的最小花费是多少

题解:首先有一个小tip,对于增删字母的花费,我们每次只会选择最小的花费,所以增删的费用中,我们只需保存最小的在add[i]。我们设dp[i][j]表示字符串从i到j变为回文串的最小花费。

然后有3种情况:1.s[i]=s[j],dp[i][j]=dp[i+1][j-1];

          2.如果dp[i+1][j]是(变为)回文串(的消费),则dp[i][j]=dp[i+1][j]+add[i];

          3.如果dp[i][j-1]是(变为)回文串(的消费),则dp[i][j]=dp[i][j-1]+add[j];

注:dp后都能变为回文串。再次强调,dp中初始化很重要。
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<iostream>  
  4. #include<algorithm>  
  5. #include<cmath>  
  6. #include<queue>  
  7. #include<cstdlib>  
  8. #include<string>  
  9. using namespace std;  
  10.   
  11. #define N 2010  
  12. #define inf 0x3f3f3f3f  
  13.   
  14. int m,n,ad,de;  
  15. int add[30];  
  16. int s[N];  
  17. int dp[N][N];  
  18.   
  19. int main()  
  20. {  
  21.     while(~scanf("%d%d",&n,&m))  
  22.     {  
  23.         char ch;  
  24.         char c[2];  
  25.         memset(s,-1,sizeof(s));  
  26.         for(int i=0;i<m;i++)  
  27.         {  
  28.             cin>>ch;  
  29.             s[i]=ch-'a';  
  30.         }  
  31.         for(int i=0;i<n;i++)  
  32.         {  
  33.             cin>>c;  
  34.             int cc=c[0]-'a';  
  35.             cin>>ad>>de;  
  36.             add[cc]=min(ad,de);  
  37.         }  
  38.         memset(dp,0,sizeof(dp));  
  39.         for(int k=1;k<m;k++)  
  40.         {  
  41.             for(int i=0,j=k;j<m;i++,j++)  
  42.             {  
  43.                 dp[i][j]=inf;  
  44.                 if(s[i]==s[j])  
  45.                     dp[i][j]=dp[i+1][j-1];  
  46.                 else  
  47.                 {  
  48.                     dp[i][j]=min(dp[i][j-1]+add[s[j]],dp[i][j]);  
  49.                     dp[i][j]=min(dp[i+1][j]+add[s[i]],dp[i][j]);  
  50.                 }  
  51.             }  
  52.         }  
  53.        printf("%d\n",dp[0][m-1]);  
  54.     }  
  55.     return 0;  
  56. }  

四、瞅啥呢?原题目链接在这里
Language:
Brackets Sequence
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 28991 Accepted: 8247 Special Judge

Description

Let us define a regular brackets sequence in the following way: 

1. Empty sequence is a regular sequence. 
2. If S is a regular sequence, then (S) and [S] are both regular sequences. 
3. If A and B are regular sequences, then AB is a regular sequence. 

For example, all of the following sequences of characters are regular brackets sequences: 

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

And all of the following character sequences are not: 

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

Some sequence of characters '(', ')', '[', and ']' is given. You are to find the shortest possible regular brackets sequence, that contains the given character sequence as a subsequence. Here, a string a1 a2 ... an is called a subsequence of the string b1 b2 ... bm, if there exist such indices 1 = i1 < i2 < ... < in = m, that aj = bij for all 1 = j = n.

Input

The input file contains at most 100 brackets (characters '(', ')', '[' and ']') that are situated on a single line without any other characters among them.

Output

Write to the output file a single line that contains some regular brackets sequence that has the minimal possible length and contains the given sequence as a subsequence.

Sample Input

([(]

Sample Output

()[()]

Source


题意:根据题目所给的字符串添加括号,输出最短的匹配括号串。(题目隐含给出的字符串包含空格,所以输入的时候必须用gets输入,scanf 是以回车和空格作为结束符号,gets只以回车作为结束)

题解:1.用结构体来保存(从 i 到 j 所需添加的最少括号的数目dp[ i ][ j ].t、添加括号后构成的新的括号串dp[ i ][ j ].s),string 可以字符串之间直接相加减。
            2.当s[i]==s[j] 时,dp[ i ][ j ].t=min(dp[ i ][ j ].t,dp[ i+1 ][ j -1].t) ,否则,从 i 到 j 之间枚举k,dp[ i ][ j ].t=dp[ i ][ k ].t+dp[ k+1 ][ j ].t .

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<iostream>  
  4. #include<algorithm>  
  5. #include<cmath>  
  6. #include<queue>  
  7. #include<cstdlib>  
  8. #include<string>  
  9. using namespace std;  
  10.   
  11. #define N 2010  
  12. #define inf 99999999  
  13.   
  14. struct node  
  15. {  
  16.     int t;  
  17.     string s;  
  18. }dp[110][110];  
  19.   
  20. char str[110];  
  21. string ss;  
  22.   
  23. void init(int l)  
  24. {  
  25.     for(int i=0; i<l; i++)  
  26.     {  
  27.         for(int j=i; j<l; j++)  
  28.         {  
  29.             dp[i][j].s="";  
  30.             dp[i][j].t=inf;  
  31.         }  
  32.     }  
  33.     for(int i=0; i<l; i++)  
  34.     {  
  35.         dp[i][i].t=1;  
  36.         if(ss[i]=='('||ss[i]==')')  
  37.             dp[i][i].s="()";  
  38.         else  
  39.             dp[i][i].s="[]";  
  40.     }  
  41. }  
  42. int main()  
  43. {  
  44.     while(gets(str))  
  45.     {  
  46.         ss=str;  
  47.         int l=ss.size();  
  48.         init(l);  
  49.   
  50.         for(int i=l-1; i>=0; i--)  
  51.         {  
  52.             for(int j=i+1; j<l; j++)  
  53.             {  
  54.                 if((ss[i]=='('&&ss[j]==')')||(ss[i]=='['&&ss[j]==']'))  
  55.                 {  
  56.                     if(dp[i][j].t > dp[i+1][j-1].t)  
  57.                     {  
  58.                      dp[i][j].t=dp[i+1][j-1].t;  
  59.                      dp[i][j].s=ss[i]+dp[i+1][j-1].s+ss[j];  
  60.                     }  
  61.   
  62.                 }  
  63.                 for(int k=i; k<j; k++)  
  64.                 {  
  65.                     if(dp[i][j].t > dp[i][k].t+dp[k+1][j].t)  
  66.                     {  
  67.                         dp[i][j].t=dp[i][k].t+dp[k+1][j].t;  
  68.                         dp[i][j].s=dp[i][k].s+dp[k+1][j].s;  
  69.                     }  
  70.                 }  
  71.             }  
  72.         }  
  73.         cout<<dp[0][l-1].s<<endl;  
  74.     }  
  75.     return 0;  
  76. }  


Language:
Running
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 5853 Accepted: 2198

Description

The cows are trying to become better athletes, so Bessie is running on a track for exactly N (1 ≤ N ≤ 10,000) minutes. During each minute, she can choose to either run or rest for the whole minute.

The ultimate distance Bessie runs, though, depends on her 'exhaustion factor', which starts at 0. When she chooses to run in minute i, she will run exactly a distance of Di (1 ≤ Di ≤ 1,000) and her exhaustion factor will increase by 1 -- but must never be allowed to exceed M (1 ≤ M ≤ 500). If she chooses to rest, her exhaustion factor will decrease by 1 for each minute she rests. She cannot commence running again until her exhaustion factor reaches 0. At that point, she can choose to run or rest.

At the end of the N minute workout, Bessie's exaustion factor must be exactly 0, or she will not have enough energy left for the rest of the day.

Find the maximal distance Bessie can run.

Input

* Line 1: Two space-separated integers: N and M
* Lines 2..N+1: Line i+1 contains the single integer: Di

Output

* Line 1: A single integer representing the largest distance Bessie can run while satisfying the conditions.
 

Sample Input

5 2
5
3
4
2
10

Sample Output

9

Source



题意:求在给出的n分钟内,跑的最远距离。num[ i ] 表示第i 分钟跑步的距离,m表示最大的疲劳值,每跑一分钟,疲劳值+1,每休息一分钟,疲劳值-1,不过一开始休息后,必须休息到疲劳值为0的时候,才能重新开始跑步,疲劳值为0 的时候也可以继续休息。

题解:用dp[ i ][ j ] 表示第i分钟疲劳值为j时跑的最大距离。则dp[ i ][ 0 ]有两种方式可以达到,1.疲劳值为0仍继续休息
dp[i][0]=dp[i-1][0];    2.第i-k分钟的疲劳值为k,然后休息k分钟后有 dp[ i ][ 0 ]=dp[ i-k ][ k ];         跑步时的距离dp[ i ][ j ]=dp[ i-1 ][ j-1 ]+num[ i ] ;

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<iostream>  
  4. #include<algorithm>  
  5. #include<cmath>  
  6. #include<queue>  
  7. #include<cstdlib>  
  8. #include<string>  
  9. using namespace std;  
  10.   
  11. #define N 2010  
  12. #define inf 99999999  
  13.   
  14. int dp[10005][505];  
  15. int num[10005];  
  16.   
  17. int main()  
  18. {  
  19.     int n,m;  
  20.     while(~scanf("%d%d",&n,&m))  
  21.     {  
  22.         for(int i=1; i<=n; i++)  
  23.         {  
  24.             scanf("%d",&num[i]);  
  25.         }  
  26.   
  27.         memset(dp,0,sizeof(dp));  
  28.   
  29.         for(int i=1; i<=n; i++)  
  30.         {  
  31.             for(int j=1; j<=m; j++)           //初始化取值,任意时间任意疲劳数的初值,方便后面的调用。  
  32.             {  
  33.                 dp[i][j]=dp[i-1][j-1]+num[i];  
  34.             }  
  35.   
  36.             dp[i][0]=dp[i-1][0];             //两种情况的判定,疲劳数变为0的情况。  
  37.   
  38.             for(int k=1; k<=m; k++)  
  39.             {  
  40.                 if(i-k>0)  
  41.                     dp[i][0]=max(dp[i][0],dp[i-k][k]);   //保证最后的疲劳数为0的情况下,获得最大的值。  
  42.             }  
  43.         }  
  44.         printf("%d\n",dp[n][0]);  
  45.     }  
  46.     return 0;  
  47. }  
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
区间DP是一种动态规划的方法,用于解决区间范围内的问题。在Codeforces竞赛区间DP经常被用于解决一些复杂的字符串或序列相关的问题。 在区间DPdp[i][j]表示第一个序列前i个元素和第二个序列前j个元素的最优解。具体的转移方程会根据具体的问题而变化,但是通常会涉及到比较两个序列的元素是否相等,然后根据不同的情况进行状态转移。 对于区间长度为1的情况,可以先进行初始化,然后再通过枚举区间长度和区间左端点,计算出dp[i][j]的值。 以下是一个示例代码,展示了如何使用区间DP来解决一个字符串匹配的问题: #include <cstdio> #include <cstring> #include <string> #include <iostream> #include <algorithm> using namespace std; const int maxn=510; const int inf=0x3f3f3f3f; int n,dp[maxn][maxn]; char s[maxn]; int main() { scanf("%d", &n); scanf("%s", s + 1); for(int i = 1; i <= n; i++) dp[i][i] = 1; for(int i = 1; i <= n; i++) { if(s[i] == s[i - 1]) dp[i][i - 1] = 1; else dp[i][i - 1] = 2; } for(int len = 3; len <= n; len++) { int r; for(int l = 1; l + len - 1 <= n; l++) { r = l + len - 1; dp[l][r] = inf; if(s[l] == s[r]) dp[l][r] = min(dp[l + 1][r], dp[l][r - 1]); else { for(int k = l; k <= r; k++) { dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]); } } } } printf("%d\n", dp[n]); return 0; } 希望这个例子能帮助你理解区间DP的基本思想和应用方法。如果你还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值