目录
概念
区间dp就是在区间上进行动态规划,求解一段区间上的最优解。主要是通过合并小区间的 最优解进而得出整个大区间上最优解的dp算法。
所以在代码实现上,我可以枚举区间长度len为每次分割成的小区间长度(由短到长不断合并),内层枚举该长度下可以的起点,自然终点也就明了了。
区间DP,最常用的一种策略(类似于数学归纳法)
区间DP,通常的答案是dp[1][n]。
例题1石子合并
题目链接:https://www.acwing.com/problem/content/284/
题目
设有 N N 堆石子排成一排,其编号为 1,2,3,…,N 1,2,3,…,N 。
每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N N 堆石子合并成为一堆。
每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。
例如有 4 4 堆石子分别为
1 3 5 2
, 我们可以先合并 1、2 1、2 堆,代价为 4 4 ,得到4 5 2
, 又合并 1,2 1,2 堆,代价为 9 9 ,得到9 2
,再合并得到 11 11 ,总代价为 4+9+11=24 4+9+11=24 ;如果第二步是先合并 2,3 2,3 堆,则代价为 7 7 ,得到
4 7
,最后一次合并代价为 11 11 ,总代价为 4+7+11=22 4+7+11=22 。问题是:找出一种合理的方法,使总的代价最小,输出最小代价。
输入格式
第一行一个数 N N 表示石子的堆数 N N 。
第二行 N N 个数,表示每堆石子的质量(均不超过 1000 1000 )。
输出格式
输出一个整数,表示最小代价。
数据范围
1≤N≤300 1≤N≤300
输入样例:
4 1 3 5 2
输出样例:
22
k是 [l,r] 区间内任意一个点,把原本的区间分为两个部分:[ l ,k] 和 [ k+1, r ] ,而每个区间都是由这之前的两个合并起来的,顺便加上二者的数值(这里 前缀和的好处就来了!)
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
const int N=1010;
int f[N][N],s[N];
int n,m;
int main()
{
while(cin>>n)
{
memset(s,0,sizeof(s));//前缀和
for(int i=1;i<=n;i++)
{
// cin>>a[i];
cin>>s[i];
s[i]=s[i]+s[i-1];
}
for(int len=2;len<=n;len++)//区间长度
{
for(int i=1;i+len-1<=n;i++)//区间的左右端点
{
int l=i,r=i+len-1;
f[l][r]=1e8;//要求最小先初始化为最大值
for(int k=l;k<r;k++)
{
f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
}
}
}
cout<<f[1][n]<<endl;
}
return 0;
}
例题2Cheapest Palindrome
题目链接:
http://poj.org/problem?id=3280
Cheapest Palindrome
Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 17665 Accepted: 8212 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
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
const int inf=0x3f3f3f;
int f[2010][2010],cost[60];
char s[2010];
int n,m;
int main()
{
while(cin>>n>>m)
{
cin>>s;
char e;
int x,y;
memset(cost,0,sizeof(cost));
for(int i=0;i<n;i++)
{
cin>>e>>x>>y;
cost[e-'a']=min(x,y);
}
memset(f,0,sizeof(f));
for(int len=1;len<m;len++)//区间长度
{
for(int i=0,j=len;j<m;i++,j++)//区间的左右端点
{
f[i][j]=inf;
if(s[i]==s[j])
f[i][j]=f[i+1][j-1];
else
{
f[i][j]=min(f[i][j],min(f[i+1][j]+cost[s[i]-'a'],f[i][j-1]+cost[s[j]-'a']));
}
}
}
cout<<f[0][m-1]<<endl;
}
return 0;
}
例题3Two Rabbits
题目链接:https://acm.hdu.edu.cn/showproblem.php?pid=4745
Two Rabbits
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 4103 Accepted Submission(s): 2078
Problem DescriptionLong long ago, there lived two rabbits Tom and Jerry in the forest. On a sunny afternoon, they planned to play a game with some stones. There were n stones on the ground and they were arranged as a clockwise ring. That is to say, the first stone was adjacent to the second stone and the n-th stone, and the second stone is adjacent to the first stone and the third stone, and so on. The weight of the i-th stone is ai.
The rabbits jumped from one stone to another. Tom always jumped clockwise, and Jerry always jumped anticlockwise.
At the beginning, the rabbits both choose a stone and stand on it. Then at each turn, Tom should choose a stone which have not been stepped by itself and then jumped to it, and Jerry should do the same thing as Tom, but the jumping direction is anti-clockwise.
For some unknown reason, at any time , the weight of the two stones on which the two rabbits stood should be equal. Besides, any rabbit couldn't jump over a stone which have been stepped by itself. In other words, if the Tom had stood on the second stone, it cannot jump from the first stone to the third stone or from the n-the stone to the 4-th stone.
Please note that during the whole process, it was OK for the two rabbits to stand on a same stone at the same time.
Now they want to find out the maximum turns they can play if they follow the optimal strategy.InputThe input contains at most 20 test cases.
For each test cases, the first line contains a integer n denoting the number of stones.
The next line contains n integers separated by space, and the i-th integer ai denotes the weight of the i-th stone.(1 <= n <= 1000, 1 <= ai <= 1000)
The input ends with n = 0.OutputFor each test case, print a integer denoting the maximum turns.Sample Input1 1 4 1 1 2 1 6 2 1 1 2 1 3 0Sample Output1 4 5HintFor the second case, the path of the Tom is 1, 2, 3, 4, and the path of Jerry is 1, 4, 3, 2. For the third case, the path of Tom is 1,2,3,4,5 and the path of Jerry is 4,3,2,1,5.SourceRecommendliuyiding
这题可以简化为求环上的最长回文,为了方便操作直接将原序列复制一份到后面,然后在这个长度为2*n的序列上求一遍区间最长回文就可以
DP[i][j]表示区间[i,j]的最长回文串,可以转移当a[i]==a[j],DP[i][j]=DP[i+1][j-1]+2
然后在所有长度范围为n的区间里找一个最大的即可
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define MAXN 1005
using namespace std;
int s[MAXN],dp[MAXN][MAXN];
int main()
{
int n,i,j,k;
while(~scanf("%d",&n)&&n)
{
memset(dp,0,sizeof(dp));
for(i=1; i<=n; ++i)
{
scanf("%d",&s[i]);
dp[i][i]=1;
}
for(i=2; i<=n; ++i)
for(j=1; j+i-1<=n; ++j)
{
k=j+i-1;
dp[j][k]=max(dp[j][k],max(dp[j+1][k],dp[j][k-1]));
if(s[j]==s[k])
dp[j][k]=max(dp[j][k],dp[j+1][k-1]+2);
}
int ans=0;
for(i=1; i<=n; ++i)
ans=max(ans,dp[1][i]+dp[i+1][n]);
printf("%d\n",ans);
}
return 0;
}