区间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])
}
}
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 charactersa1a2 … an, 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 < … < 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[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;
}
Description
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
Output
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;
}
Description
Input
The next n line are n integer D1-Dn means the value of diaosi of boys (0 <= Di <= 100)
Output
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;
}
Description
Input
The first line contains string A.
The second line contains string B.
The length of both strings will not be greater than 100.
Output
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很难想到,还是要多做才有经验。。。