\(Des\)
• 有一排数量为N的方块,每次可以把连续的相同颜色的区间消除,得到分数为 区间长度的平方,然后左右两边连在一起,问最大分数为多少。
• n<=1
\(Sol\)
正解状态设得奇奇怪怪巧巧妙妙.
\(f[i][j][k]\)表示区间\([i,j]\)且\(j\)右边还有\(k\)个和\(j\)同色的方块,消除所有这些的最大分数.转移分为两种:
1.将右端点和多出的\(k\)个一起消掉,分数为\(f[i][j-1]+(k+1)^2\).
2.选一个与右端点同色的点\(p\),消掉\([p+1,j-1]\)使得\(p,j\)相邻,分数为\(f[i][p][k+1]+f[p+1][j-1][0]\).
那怎么样想到这个巧巧妙妙的状态呢:
首先这个题是有合并顺序的,可以考虑区间\(dp\),考虑对于区间\([l,r]\)能不能通过枚举中间点\(k\),用\([l,k],[k+1][r]\)转移.稍加思考就可以知道,这个亚子是不行的.这个题是"消消乐",可能中间的某个点会和边界一起消掉更优.于是就发现,这题并不是规规矩矩的区间$dp $,看能不能通过多记一维状态来解决这个问题...于是就聪聪明明地设出了这个状态强行.
这题好好写,瞎写一下就过了.
\(Code\)
Code
```cpp
#include
#include
#include
#define il inline
#define Ri register int
#define go(i,a,b) for(Ri i=a;i<=b;++i)
#define yes(i,a,b) for(Ri i=a;i>=b;--i)
#define e(i,u) for(Ri i=b[u];i;i=a[i].nt)
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#define db double
#define inf 2147483647
using namespace std;
il int read()
{
Ri x=0,y=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*y;
}
const int N=210;
int T,n,a[N],s[N],f[N][N][N];
int main()
{
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
T=read();
go(TT,1,T)//remember to init!
{
n=read();
go(i,1,n)a[i]=read();
mem(s,0);mem(f,0);
go(i,1,n)
go(j,i+1,n)
if(a[i]==a[j])s[i]++;
yes(i,n,1)
go(j,i,n)
go(k,0,s[j])
{
f[i][j][k]=f[i][j-1][0]+(k+1)*(k+1);
go(p,i,j-1)
if(a[p]==a[j])
f[i][j][k]=max(f[i][j][k],f[i][p][k+1]+f[p+1][j-1][0]);
}
printf("Case %d: %d\n",TT,f[1][n][0]);
}
return 0;
}
```