区间DP

                                                 区间DP

 

【个人理解】

我觉得所有的DP都是优化的枚举(可能学的少,至少线性DP我觉得是),把一开始的状态结果保存到到数组中,然后推导后面的状态。我觉得区间DP同理,也是一个由短区间推导长区间的一个过程。最典型的例子就是下面的合并石子。

 

【实现分析】

所有DP关键的都是要找状态转移方程,区间DP做好有一个前提:阶段,状态,决策,或与现在不太理解,但下面的例题会有注释,去仔细揣摩和体会就行。

 

【代码模板】

memset(do,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
    dp[i][i]=初始值
}
for(int len=2;len<=n;len++)  //区间长度
for(int i=1;i<=n;i++)        //枚举起点
{
    int j=i+len-1;           //区间终点
    if(j>n) break;           //越界结束
    for(int k=i;k

 

【经典例题】

区间DP的基本模型有:

石子归并,括号匹配,整数划分,求回文串,下面经典例题都有涉及。

旁边有目录,可以使用它找题,

 

石子合并

石子合并有三种类型,

第一种任意位置合并,那个简单贪心做,就行,例题:洛谷 P1090 合并果子。

第二种直线性相邻合并,对应着下面的第一个。

第二种环形下的相邻合并,对应这第二个。

 

石子合并(一)(直线相邻)

时间限制:1000 ms  |  内存限制:65535 KB

难度:3

描述 

N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。

输入

有多组测试数据,输入到文件结束。
每组测试数据第一行有一个整数n,表示有n堆石子。
接下来的一行有n0< n <200)个数,分别表示这n堆石子的数目,用空格隔开

输出

输出总代价的最小值,占单独的一行

样例输入

3

1 2 3

7

13 7 8 16 21 4 18

样例输出

9

239

 

分析:

如果我们可以把第l堆石子和第r堆石子合并起来,则说明第l堆石子和第r堆石子之间的每一堆都被合并了,这样l和r才能相邻,所以,任意时刻,任意一堆石子均可以用闭区间[l,r]表示,表示这堆石子是由第l~r堆石子合并而来,重量为a[l]+a[l+1]+……a[r].还有,一定存在一个整数k(l<=k

从上述分析,我们就可以得到一个对应的动态规划的性质,两个长度较小的区间信息向一个更长的区间发生了转移,划分点k就产生了转移决策。自然的,我们要把区间长度len先由小到大枚举(高深点就是把区间长度作为阶段),保证后面的长区间由短区间得到,然后我们应该枚举l(左端点),并找到相应的右端点l+len-1(对应着状态),枚举中间断点k,f[l][r]表示第l堆合并到第r堆合成一堆,需要消耗的最小力气。

对应的状态转移方程:  f[l][r]=min(f[l][r],f[l][k]+f[k+1][r])+ a[l]+a[l+1]+……a[r]

a[l]+a[l+1]+……a[r]= sum[r]-sum[l-1])用一个前缀和来维护就行,或许你不知道为什么把l~r的石子连加起来就对了,不是应该两个合并有一个保存值吗?

例如,1,2,3.正确应该为 1+2=3 ,3+3=6,第一次合并3,第二次合并6,答案是九

而上述为什么只计算前缀和1+2+3=6,但你别忘了f[i][j]本身就是有值的,它是有区间长度为2开始一点点推出来的,f[l][r]其实我们要把它看成合并两堆的花费的力气,还是1,2,3,他就两种情况先合并1,2堆,再合并3,或者先合并2,3堆,再合并1堆,但是,无论怎么合并他合并(1,2),3堆或者1,(2,3)这两堆的力气都是1+2+3=6,而合并(1,2)花费的力气保存在f[1][2]中,合并2,3的力气保存在f[2][3]中,f[1][3]=min(f[1][2],f[2][3])+1+2+3=9。

阶段:区间长度

状态:左右端点

决策:划分点k的最佳位置

状态转移方程  f[l][r]=min(f[l][r],f[l][k]+f[k+1][r])+ sum[r]-sum[l-1])

初值:f[i][i]=0,其余为无穷大

目标:f[1][n]

代码实现:

#include 
#include 
#include 
#include 
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
 
typedef long long ll;
const int maxn = 205;
const ll mod = 1e9+7;
const ll INF = 1e18;
const double eps = 1e-9;
 
int n,x;
int sum[maxn];
int dp[maxn][maxn];
 
int main()
{
    while(~scanf("%d",&n))
    {
        sum[0]=0;
        mst(dp,0x3f);     //初始化
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            sum[i]=sum[i-1]+x; //前缀和
            dp[i][i]=0;   //初始化
        }
        for(int len=2;len<=n;len++) //阶段:区间长度
        for(int i=1;i<=n;i++)     //状态:左端点
        {
            int j=i+len-1;    //状态:右端点
            if(j>n) continue;  //决策
            for(int k=i;k

 

石子合并(二)(环形相邻)

题目:洛谷 P1880 [NOI1995]石子合并

这题与上面有什么不同呢,一个简单例子:这题的最后一个和第一个一开始就能合并,也就是说它的摆放是一个环状,而上面那一题是一排。

由于是环状,我们需要把它转换为链状,那么就把存储石子的数组a[]空间开大一倍,从n+1~2*n存储的值等于1~n存储的值,那么只需要从1n枚举链的开头即可,链尾则分别为n2*n-1。这样计算和环状是等效的。解决:

举个例子:

假如现在叫你合并1 2 3,我们就可以写出1 2 3 1 2 3,也就是说把数组扩大成一倍,这样你只要保证你选出来的数是三个就行了,你试试看。

然后你按照上面的思想做就

具体细节和代码实现:点这里


 括号匹配

题目链接   poj 2995

Brackets

Time Limit: 1000MS

 

Memory Limit: 65536K

Total Submissions: 12141

 

Accepted: 6431

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 i1, i2, …, im where 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

Stanford Local 2004

算法分析:

题意:

给出一个的只有'(',')','[',']'四种括号组成的字符串,求最多有多少个括号满足题目里所描述的完全匹配。

分析:

这题跟石子合并思路基本一模一样,其思维难度低于石子合并,同样用区间短的推导区间长的。

dp[i][j]表示区间[i,j]里最大完全匹配数。

阶段:区间长度

状态:左端点

决策:间断点k的位置(什么时候最大)

状态转移方程:

dp[i-1][j+1]=dp[i][j]+(s[i-1]s[j+1]匹配 ? 2 : 0)

dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j])

 

边界条件:全为0

目的:dp[1][n]

代码实现:

#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include
using namespace std;

typedef long long ll;
const int maxn = 105;
const ll mod = 1e9+7;
const ll INF = 1e18;
const double eps = 1e-9;
 
char s[maxn];
int dp[maxn][maxn];
 
int main()
{
    while(~scanf("%s",s+1)&&s[1]!='e')
    {
        int n=strlen(s+1);
        memset(dp,0,sizeof(dp));
        for(int len=2;len<=n;len++) //阶段
        for(int i=1;i<=n;i++)       //状态
        {
            int j=i+len-1;
            if(j>n) break;
            if(s[i]=='('&&s[j]==')'||s[i]=='['&&s[j]==']')
            {
                dp[i][j]=dp[i+1][j-1]+2;
            }
            for(int k=i;k

【变题】 题目链接

括号匹配(二)

时间限制:1000 ms  |  内存限制:65535 KB

难度:6

描述

给你一个字符串,里面只包含"(",")","[","]"四种符号,请问你需要至少添加多少个括号才能使这些括号匹配起来。
如:
[]是匹配的
([])[]是匹配的
((]是不匹配的
([)]是不匹配的

输入

第一行输入一个正整数N,表示测试数据组数(N<=10)
每组测试数据都只有一行,是一个字符串S,S中只包含以上所说的四种字符,S的长度不超过100

输出

对于每组测试数据都输出一个正整数,表示最少需要添加的括号的数量。每组测试输出占一行

样例输入

 

4

[]

([])[]

((]

([)]

样例输出

 

0

0

3

2

 

【题意】

上一题求的是满足完美匹配的最大括号数量,而这题问的是使所有括号完美匹配需要添加的最小括号数量

【分析】

很简单我们求出完美匹配的最大括号数量,用总的减去就行了,所求=原序列括号数量-最大匹配括号数量。

上题代码输出:n-dp[1][n]即可

 

括号匹配(三)输出路径

上面那个题是偷了一个懒,但下面这题就不行了。

题目链接:

http://poj.org/problem?id=1141
 

题意:

给一个由[,],{,}组成的字符串序列,求增加最少的字符,使该序列能够匹配,并输出最后的方案。

分析:

我们这里设dp[i][j]表示从i~j 所需的最少的字符使之能匹配,转移的话要么是头尾匹配直接加中间,或者分成两段。

首先判断括号是否匹配,如果匹配,那么dp [ i ] [ j ] = dp[ i + 1] [ j - 1] 。如果不匹配,就要枚举断点,找最小的了。

不过要输出到达路径,所以在用一个path[i][j]表示到达该路径时的选择,-1表示头尾,其他表示中间分开的位置。

递归输出路径。递归是个好东西,能够很大程度的改变顺序,特别是逆着的。

阶段:区间长度

状态:左端点

决策:间断点k的位置(什么时候最大)

状态转移方程:

dp[i-1][j+1]=dp[i][j]

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j])

 

边界条件:全为0dp[i][i]=1,一个东西加一个就行。

 

#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include
using namespace std;
#define Maxn 110
const int INF=(1<<30);
char sa[Maxn];
int dp[Maxn][Maxn],path[Maxn][Maxn];
//dp[i][j]表示区间i~j内需要最少的字符数能够匹配
//path[i][j]表示到达该状态是哪种情况,
//-1表示第一个和最后一个,其他表示中间的分段点,然后递归输出
//递归能够改变次序
void output(int l,int r) 
{
   if(l>r)
      return ;
   if(l==r) //到达了最后
   {
      if(sa[l]=='('||sa[l]==')')
         printf("()");
      else
         printf("[]");
      return ;
   }
   if(path[l][r]==-1) //首尾,先输出开始,然后递归输出中间,最后输出结尾
   {
      putchar(sa[l]);
      output(l+1,r-1);
      putchar(sa[r]);
   }
   else
   {
      output(l,path[l][r]);//直接递归输出两部分
      output(path[l][r]+1,r);
   }
}
int main()
{
   while(gets(sa+1)) //有空串,scanf("%s"),不能读空串,然后少一个回车,会出错
   {
      int n=strlen(sa+1);
      memset(dp,0,sizeof(dp));
      for(int i=1;i<=n;i++)
         dp[i][i]=1; //一个的话只需一个就可以匹配
      for(int len=1;len

整数划分

乘积最大

题目链接:洛谷 1018 乘积最大

题意中文题

分析:

 动态规划思想,我们按插入的乘号数来划分阶段(其实相当于合并石子的区间长度),即可把问题看成k个阶段的决策问题,设f[i][k]为前i个数插入k个乘号最大乘积,这里我们用a[j][i]表示从第j位到第i位所组成的自然数(这与以前的动态规划不同,对输入结果做改动)

根据区间DP的思想我们可以从插入较少乘号的结果算出插入较多乘号的结果。

第一个循环枚举阶段(插入的乘号数),第二个循环枚举状态(i个数,因为前i个数要插入乘号),第三层循环枚举决策(乘号放在前i个数那个位置最大)。

阶段:插入的乘号数量(数字串分成的段数);

状态:长度为i个数字;

决策乘号放在前i个数那个位置最大

状态转移方程:f[i][k]=max(f[i][k],f[j][k-1]*a[j+1][i])

边界条件:f[i][0]=a[1][i]

目标:f[n][k]

细节处理看代码:

#include   
using namespace std;  
int main()  
{  
    long long   int n,k,K,j,i,a[45][45]={0},f[450][450]={0};  
   cin>>n>>K;  
   string ch;  
   cin>>ch;  
   for(i=n;i>=1;i--)  
   {  
       a[i][i]=ch[i-1]-48;  
        
   }  
  
   for(j=2;j<=n;j++)  
    for(i=j-1;i>=1;i--)  
    a[i][j]=a[i][j-1]*10+a[j][j];      //对输入结果处理,很妙的递推,需细细体会  
    for(i=1;i<=n;i++)  
        f[i][0]=a[1][i];               //防止无乘号  
    for(k=1;k<=K;k++)                  //枚举阶段  
      for(i=k+1;i<=n;i++)             //枚举第i个数到第j个数有几个乘号的乘积,i从k+1起的原因是,插入k个乘号必须要有k+1个数  
        for(j=k;j

 

 


凸多边形三角划分问题

 

Problem Description

给定一个具有N(N<=50)个顶点(1N编号)的凸多边形,每个顶点的权值已知。问如何把这个凸多边形划分成N-2个互不相交的三角形,使得这些三角形顶点的权值的乘积之和最小。


 

Input

第一行为顶点数N,第二行为N个顶点(从1N)的权值。


 

Output
 

乘积之和的最小值。题目保证结果在int范围内。


 

Sample Input

5

1 6 4 2 1

5

121 122 123 245 231

 

 


 

Sample Output
 

34

12214884

【分析】

dp[i,j]表示从顶点i到顶点j的凸多边形三角剖分后所得到的最大乘积。

那么可以写出状态转移方程,并通过枚举分割点来转移。

阶段:区间长度(至少3ij之间至少保证一个点)

状态:左端点

决策:间断点k的位置(什么时候最大)

状态转移方程:

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

边界条件:全为0,但实现过程有一个细节,代码看

目的:dp[1][n]

代码实现:

#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include
using namespace std;
typedef long long ll;
const int maxn = 55;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
 
int a[maxn];
int dp[maxn][maxn];
 
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
       memset(dp,0,sizeof(dp));
        for(int len=3;len<=n;len++)
        for(int i=1;i<=n;i++)
        {
            int j=i+len-1;
            if(j>n) break;
            cout<


回文串系列

Palindrome subsequence(回文串个数)

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65535 K (Java/Others)
Total Submission(s): 4044    Accepted Submission(s): 1718

 

Problem Description

In mathematics, a subsequence is a sequence that can be derived from another sequence by deleting some elements without changing the order of the remaining elements. For example, the sequence is a subsequence of .
(http://en.wikipedia.org/wiki/Subsequence)

Given a string S, your task is to find out how many different subsequence of S is palindrome. Note that for any two subsequence X = and Y = , if there exist an integer i (1<=i<=k) such that xi != yi, the subsequence X and Y should be consider different even if Sxi = Syi. Also two subsequences with different length should be considered different.

 

 

Input

The first line contains only one integer T (T<=50), which is the number of test cases. Each test case contains a string S, the length of S is not greater than 1000 and only contains lowercase letters.

 

 

Output

For each test case, output the case number first, then output the number of different subsequence of the given string, the answer should be module 10007.

 

 

Sample Input

4

a

aaaaa

goodafternooneveryone

welcometoooxxourproblems

 

 

Sample Output

Case 1: 1

Case 2: 31

Case 3: 421

Case 4: 960

 

 

Source

2013 Multi-University Training Contest 4

 

 

Recommend

zhuyuanchen520   |   We have carefully selected several similar problems for you:  6408 6407 6406 6405 6404 

算法分析:

题意:给定一个字符串,求回文子序列个数,最后的答案要%10007

首先定义f数组f[l][r]表示l~r区间的回文子序列个数,还是用小区间推导大区间。

第一层循环依旧枚举阶段:区间长度。第二层循环枚举状态:开始断点,这题比上面简单点,不需要枚举决策:断点k了,请看下面分析。

1.显然根据容斥原理f[l][r]=f[l][r-1]+f[l+1][r]-f[l+1][r-1]  (因为中间的个数会算两遍);

2.我们考虑s[l]==s[r]的情况,如果这两个位置相等,那么l+1 ~ r-1这个区间的所有子序列,都可以加入lr这两个元素,构成一个新的回文子序列,除此之外 lr这两个元素也可以构成一个回文子序列那么 if  (s[l]==s[r]) f[l][r]+=f[l+1][r-1]+1;

要点

阶段:区间长度

状态:开始端点

决策:两种情况

状态转移方程:

f[l][r]=f[l][r-1]+f[l+1][r]-f[l+1][r-1]  

if  (s[l]==s[r]) f[l][r]+=f[l+1][r-1]+1;

初始条件:f[i][i]=1,其余为0

目的:f[1][n]

 

注意!!!!

做减法的时候,可能会有负数,为了使%不出现问题,我们需要先加mod%mod

代码实现:

#include
#include
#include
#include
#include
 
using namespace std;
 
inline int read()
{
   int x=0,f=1;char ch=getchar();
   while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
   while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
   return x*f;
}
const int maxn = 2010;
const int mod = 10007;
 
int f[maxn][maxn];
char s[maxn];
int tmp;
int t;
int n;
 
int main()
{
  scanf("%d",&t);
  while (t--)
  {
      tmp++;
      scanf("%s",s+1);
      n=strlen(s+1);
      memset(f,0,sizeof(f));
      for (int i=1;i<=n;++i)
        f[i][i]=1;
  
      for (int i=1;i<=n;++i)
        {
            for (int l=1;l<=n-i+1;++l)
            {
                int r=l+i-1;
                if (s[l]==s[r])
                  f[l][r]=((long long)f[l+1][r]+f[l][r-1]+1)%mod;
                else
                  f[l][r]=((long long)f[l+1][r]+f[l][r-1]-f[l+1][r-1]+mod)%mod;
            }
        }
      printf("Case %d: %d\n",tmp,f[1][n]%mod);
  }
  return 0;
}

Two Rabbits(最长回文子串的长度

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 2398    Accepted Submission(s): 1244

 

Problem Description

Long 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.

 

 

Input

The 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.

 

 

Output

For each test case, print a integer denoting the maximum turns.

 

 

Sample Input

1

1

4

1 1 2 1

6

2 1 1 2 1 3

0

 

 

Sample Output

1

4

5

Hint

 

For 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.

 

 

 

Source

2013 ACM/ICPC Asia Regional Hangzhou Online

 

 

Recommend

liuyiding   |   We have carefully selected several similar problems for you:  6408 6407 6406 6405 6404 

题意:

给定一个环状数列,求一个最长的回文子序列

分析:

环状数列很好解决,直接把数组扩开一倍即可。

我们定义f[l][r]表示l到r区间的最长非连续的的回文子序列总体长度

如果s[l]==s[r] f[l][r]=f[l+1][r-1]+2;

不然 f[l][r]=max(f[l+1][r],f[l][r-1]);

要点

阶段:区间长度

状态:开始端点

决策:两种情况

状态转移方程:

s[l]==s[r] f[l][r]=f[l+1][r-1]+2;

不然 f[l][r]=max(f[l+1][r],f[l][r-1]);

初始条件:f[i][i]=1,其余为0

目的:长度为n的所有dp[i][j]

但上述仅仅是求出一个序列的非连续最长回文子序列,题目的序列是环状的,先上正确答案代码

for (int i = 1; i <= n; i++)
		{
			//cout<

我们想一下因为他是一个环,1~i之间分两份,i~n之间分两分

则b+c和a+d构成一个更大的回文串

代码实现:

#include
#include
#include
#include
#include
#include
#include
#include
#include
 
using namespace std;
 
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
 
const int maxn = 3010;
 
int a[maxn],f[maxn][maxn];
int n,m;
int ans;
 
int main()
{ 
  while (1)
  {
  scanf("%d",&n);
  memset(f,0,sizeof(f));
  memset(a,0,sizeof(a));
  if (n==0) return 0;
  ans=0;      
  for (int i=1;i<=n;i++)
    scanf("%d",&a[i]),a[i+n]=a[i];
  for (int i=1;i<=2*n;i++)
    f[i][i]=1;
 
  for (int i=2;i<=2*n;i++)
  {
       for (int l=1;l<=2*n-i+1;l++)
       {
           int r=l+i-1;
           if (a[l]==a[r]) f[l][r]=max(f[l][r],f[l+1][r-1]+2);
           else f[l][r]=max(f[l][r-1],f[l+1][r]);
       }
  }
  for (int i=1;i<=n;i++)
  {
      ans=max(ans,f[1][i]+f[i+1][n]);

  }
  printf("%d\n",ans);
  }
  return 0;
}

 

你可能感兴趣的:(模板,2018暑假ACM集训,动态规划——区间DP,算法基础--动态规划)