区间dp
一、题目链接
Brackets
Description We give the following inductive definition of 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, …, imwhere 1 ≤ i1 < i2 < … < im ≤ n, ai1ai2 … aim is a regular brackets sequence. Given the initial sequence Input The input test file will contain multiple test cases. Each input test case consists of a single line containing only the characters 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]);
- #include<cstdio>
- #include<cstring>
- #include<iostream>
- #include<algorithm>
- #include<cmath>
- #include<queue>
- #include<cstdlib>
- #include<string>
- using namespace std;
- #define N 110
- char str[N];
- int dp[N][N];
- bool check(int i,int j)
- {
- if(str[i]=='['&&str[j]==']') return true;
- if(str[i]=='('&&str[j]==')') return true;
- return false;
- }
- int main()
- {
- while(~scanf("%s",str))
- {
- if(str[0]=='e')
- break;
- memset(dp,0,sizeof(dp));
- int l=strlen(str);
- for(int i=0;i<l-1;i++)
- if(check(i,i+1)) dp[i][i+1]=2;
- for(int n=3;n<=l;n++)
- {
- for(int i=0; i+n-1<l; i++)
- {
- if(check(i,i+n-1)) dp[i][i+n-1]=dp[i+1][i+n-2]+2;
- for(int j=i;j<i+n-1;j++)
- dp[i][i+n-1]=max(dp[i][i+n-1],dp[i][j]+dp[j+1][i+n-1]);
- }
- }
- printf("%d\n",dp[0][l-1]);
- }
- return 0;
- }
二、记忆化搜索的方法。
整合在一起的代码是 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);
- #include<cstdio>
- #include<cstring>
- #include<iostream>
- #include<algorithm>
- #include<cmath>
- #include<queue>
- #include<cstdlib>
- #include<string>
- using namespace std;
- #define N 110
- char str[N];
- int dp[N][N];
- bool check(int i,int j)
- {
- if(str[i]=='['&&str[j]==']') return true;
- if(str[i]=='('&&str[j]==')') return true;
- return false;
- }
- int solve(int i,int j)
- {
- if(dp[i][j]>=0) return dp[i][j];
- if(j<=i) return dp[i][j]=0;
- if(j==i+1)
- {
- if(check(i,j))
- return dp[i][j]=2;
- else
- return dp[i][j]=0;
- }
- dp[i][j]=solve(i+1,j); //不满足匹配的条件时的值。
- for(int k=i+1;k<=j;k++)
- {
- if(check(i,k))
- dp[i][j]=max(dp[i][j],solve(i+1,k-1)+solve(k+1,j)+2);
- }
- return dp[i][j];
- }
- int main()
- {
- while(~scanf("%s",str))
- {
- if(str[0]=='e')
- break;
- memset(dp,-1,sizeof(dp));
- int l=strlen(str);
- printf("%d\n",solve(0,l-1));
- }
- return 0;
- }
二、想看题目?戳我呀
Language:
Multiplication Puzzle
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 If he would take the cards in the opposite order, i.e. 50, then 20, then 1, the score would be 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;
注意初始化,动态规划中的初始化需要特别重视,初始化出现错误,结果就会错。
- #include<cstdio>
- #include<cstring>
- #include<iostream>
- #include<algorithm>
- #include<cmath>
- #include<queue>
- #include<cstdlib>
- #include<string>
- using namespace std;
- #define N 105
- #define inf 99999999
- int num[N];
- int dp[N][N];
- int main()
- {
- int n;
- while(~scanf("%d",&n))
- {
- for(int i=1;i<=n;i++)
- scanf("%d",&num[i]);
- for(int i=0;i<=N;i++)
- for(int j=0;j<=N;j++)
- dp[i][j]=inf;
- for(int i=1;i<n;i++)
- {
- dp[i][i+2]=num[i]*num[i+1]*num[i+2];
- dp[i][i]=dp[i][i+1]=0;
- }
- for(int k=4;k<=n;k++)
- {
- for(int i=1;i+k-1<=n;i++)
- {
- for(int j=i+1;j<i+k-1;j++)
- 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]);
- }
- }
- printf("%d\n",dp[1][n]);
- }
- return 0;
- }
三、想看题目?戳我呀
Language:
Cheapest Palindrome
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];
- #include<cstdio>
- #include<cstring>
- #include<iostream>
- #include<algorithm>
- #include<cmath>
- #include<queue>
- #include<cstdlib>
- #include<string>
- using namespace std;
- #define N 2010
- #define inf 0x3f3f3f3f
- int m,n,ad,de;
- int add[30];
- int s[N];
- int dp[N][N];
- int main()
- {
- while(~scanf("%d%d",&n,&m))
- {
- char ch;
- char c[2];
- memset(s,-1,sizeof(s));
- for(int i=0;i<m;i++)
- {
- cin>>ch;
- s[i]=ch-'a';
- }
- for(int i=0;i<n;i++)
- {
- cin>>c;
- int cc=c[0]-'a';
- cin>>ad>>de;
- add[cc]=min(ad,de);
- }
- memset(dp,0,sizeof(dp));
- for(int k=1;k<m;k++)
- {
- for(int i=0,j=k;j<m;i++,j++)
- {
- dp[i][j]=inf;
- if(s[i]==s[j])
- dp[i][j]=dp[i+1][j-1];
- else
- {
- dp[i][j]=min(dp[i][j-1]+add[s[j]],dp[i][j]);
- dp[i][j]=min(dp[i+1][j]+add[s[i]],dp[i][j]);
- }
- }
- }
- printf("%d\n",dp[0][m-1]);
- }
- return 0;
- }
四、瞅啥呢?原题目链接在这里
Language:
Brackets Sequence
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只以回车作为结束)
- #include<cstdio>
- #include<cstring>
- #include<iostream>
- #include<algorithm>
- #include<cmath>
- #include<queue>
- #include<cstdlib>
- #include<string>
- using namespace std;
- #define N 2010
- #define inf 99999999
- struct node
- {
- int t;
- string s;
- }dp[110][110];
- char str[110];
- string ss;
- void init(int l)
- {
- for(int i=0; i<l; i++)
- {
- for(int j=i; j<l; j++)
- {
- dp[i][j].s="";
- dp[i][j].t=inf;
- }
- }
- for(int i=0; i<l; i++)
- {
- dp[i][i].t=1;
- if(ss[i]=='('||ss[i]==')')
- dp[i][i].s="()";
- else
- dp[i][i].s="[]";
- }
- }
- int main()
- {
- while(gets(str))
- {
- ss=str;
- int l=ss.size();
- init(l);
- for(int i=l-1; i>=0; i--)
- {
- for(int j=i+1; j<l; j++)
- {
- if((ss[i]=='('&&ss[j]==')')||(ss[i]=='['&&ss[j]==']'))
- {
- if(dp[i][j].t > dp[i+1][j-1].t)
- {
- dp[i][j].t=dp[i+1][j-1].t;
- dp[i][j].s=ss[i]+dp[i+1][j-1].s+ss[j];
- }
- }
- for(int k=i; k<j; k++)
- {
- if(dp[i][j].t > dp[i][k].t+dp[k+1][j].t)
- {
- dp[i][j].t=dp[i][k].t+dp[k+1][j].t;
- dp[i][j].s=dp[i][k].s+dp[k+1][j].s;
- }
- }
- }
- }
- cout<<dp[0][l-1].s<<endl;
- }
- return 0;
- }
Language:
Running
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 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 |
- #include<cstdio>
- #include<cstring>
- #include<iostream>
- #include<algorithm>
- #include<cmath>
- #include<queue>
- #include<cstdlib>
- #include<string>
- using namespace std;
- #define N 2010
- #define inf 99999999
- int dp[10005][505];
- int num[10005];
- int main()
- {
- int n,m;
- while(~scanf("%d%d",&n,&m))
- {
- for(int i=1; i<=n; i++)
- {
- scanf("%d",&num[i]);
- }
- memset(dp,0,sizeof(dp));
- for(int i=1; i<=n; i++)
- {
- for(int j=1; j<=m; j++) //初始化取值,任意时间任意疲劳数的初值,方便后面的调用。
- {
- dp[i][j]=dp[i-1][j-1]+num[i];
- }
- dp[i][0]=dp[i-1][0]; //两种情况的判定,疲劳数变为0的情况。
- for(int k=1; k<=m; k++)
- {
- if(i-k>0)
- dp[i][0]=max(dp[i][0],dp[i-k][k]); //保证最后的疲劳数为0的情况下,获得最大的值。
- }
- }
- printf("%d\n",dp[n][0]);
- }
- return 0;
- }