题解 P3146 【[USACO16OPEN]248 G】

· · 题解

题解 P3146 【[USACO16OPEN]248 G】

详细的讲一下该题。

一、题意

一个数组,两个相同的相邻数字可以组合成一个比这两个数字原来+1的数。就像下面这个数组的组合操作:

1 1 2 3

 |

\_/

2 2 3 --->把第一、第二个元素合并

要你求出该数组可以组合成的最大数字。

举个例子:

4

2 2 2 2(下面是最优值组合过程)

 |

\_/

3 2 2

 |

\_/

3 3

 |

\_/
 4  --->最优值

二、初步找算法。

对于每一个组合起来的数,我们都可以把它看做是由一堆初始数字组合而成,自然而然就想到了区间DP。

三、明确解题具体方法

for(int k=i;k<j;k++)//枚举两个小区间的分界点
  if(f[i][k]==f[k+1][j])//如果小区间最大值相同
    f[i][j]=max(f[i][j],f[i][k]+1);//更新当前区间
ans=max(ans,f[i][j]);//存最大值

四、完善细节

  1. 初始化:在读入每个元素时,将f数组的当前位置到当前位置(f [ i ] [ i ])设为元素值。

  2. 最大值在刚开始读入时就要开始更新(以免后面没得组合的情况)

五、思考争议

有人会问:如果两个小区间的最大值不一样,导致无法组合,甚至丧失最后的最优值,怎么办?

当初我也想过这个问题,没有过多的去思考,用了三维,是避免了所谓上面的问题,但是很可惜,炸时间。

那为什么还可以这样做呢?因为题目说,合并之后的数的最大值是原来的数的值+1,那比起你用非最大值合并,还不如直接取当前还没合并的最大值?

如果还没弄明白,那你可以找一些数据来摆弄摆弄就懂了。

六、完整代码

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<string>
#include<cstring>
#include<cstdlib>
using namespace std;
int n,a[248+5],f[248+5][248+5],j,ans=-1; 
int main()
{
  scanf("%d",&n);
  for(int i=1;i<=n;i++)
  {
    scanf("%d",&a[i]);
    f[i][i]=a[i];
    ans=max(ans,a[i]);
  }
  for(int l=2;l<=n;l++)
    for(int i=1;i<=n-l+1;i++)
    {
      j=i+l-1;
      for(int k=i;k<j;k++)
        if(f[i][k]==f[k+1][j])f[i][j]=max(f[i][j],f[i][k]+1);
        ans=max(ans,f[i][j]);
    }
  cout<<ans;
  return 0;
}
//代码那么简单还抄?

七、FOR管理员:

这篇题解思路可能有重复,但解题的步骤绝对是该题题解中最详细清晰的,管理员过一下,谢谢。