区间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:
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
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
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
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很难想到,还是要多做才有经验。。。