先mark一下,感觉自己好难理解。。。。正在理解中,,,,
**************************************************
几乎折腾了一下午。。。表示很痛苦。不过终于有了点理解,虽然代码可能基本就是和别人一样的了。。。。。
不过也算是学习的过程吧。。。。
然后我多打几遍在理解一下再来更新。。。。。。
*************************************************
看了好多题解,终于有了点眉目。
DP[I][J]表示前i个数中,j状态下可以得到的最大值。因为可能会爆内存,所以用了滚动数组。然后j状态存储的实际上是当前状态递减的序列,用二进制表示。因为如果当前数组末尾没有递减的数列,那么就算合并也不能再继续合并了,所以这样处理。
原先一直不理解为什么每次DP的时候要枚举到最大值,或者说不知道怎么枚举上一次得到的(剩下的)容量。然后这次好像又懂了一点点。。。如果原先出现过这样的情况,那么等到下次你枚举到这个情况的时候就会再次碰到他,这也是为什么要枚举到最大值。
DP方程用了两次,第一次是不取当前的a[i],取了当前状态的最大值。然后假设取了当前的a[i],然后看看可以合并多少次,算出得到的奖励和合并后的状态。
第二次表示合并后的那种状态是由原先某种路径得来的比较好,还是由这一次经过以上方式合并得来的比较好。
/*
3
4
2 8 4 8
5
2 2 4 8 16
8
8 4 4 2 8 4 2 2
*/
#include <stdio.h>
#include <string.h>
#define maxn 5000
int n,dp[2][maxn],a[maxn],sum,score[maxn];
int max(int x,int y)
{
if(x>y) return x;
else return y;
}
void solve()
{
int i,j,k;
int pre=0,now=1;
dp[pre][0]=0;
for(i=0;i<n;i++)
{
for(j=0;j<=4096;j++)
{
if(dp[pre][j]!=-1)
{
dp[now][j]=max(dp[now][j],dp[pre][j]);
int t=j;
k=a[i]-1;
int tmp=(1<<k)-1;
int get=score[a[i]];
if((tmp&t)==0)
{
while(t&(1<<k))
{
get+=score[k+2];
k++;
}
tmp=(1<<k)-1;
t&=~tmp;///把比k低的位数全变成0
t|=1<<k;
}
else t=1<<k;
dp[now][t]=max(dp[now][t],dp[pre][j]+get);
// printf("**%d\n",get);
}
}
now=!now;
pre=!pre;
}
for(i=0;i<=4096;i++) sum=max(sum,dp[pre][i]);
}
int main()
{
int t;
int loc[20];
scanf("%d",&t);
loc[2]=1;loc[4]=2;loc[8]=3;loc[16]=4;
score[0]=1;
for(int i=1;i<=4096;i++)
score[i]=score[i-1]*2;
while(t--)
{
scanf("%d",&n);
sum=0;
memset(dp,-1,sizeof(dp));
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
a[i]=loc[a[i]];
}
solve();
printf("%d\n",sum);
}
return 0;
}