zoj3802 Easy 2048 Again 状压dp

来源:互联网 发布:windows phone吧 编辑:程序博客网 时间:2024/05/18 02:44
Easy 2048 Again

Time Limit: 2 Seconds      Memory Limit: 65536 KB

Dark_sun knows that on a single-track road (which means once he passed this area, he cannot come back again), there are some underground treasures on each area of the road which has the value of 2, 4, 8 or 16. Dark_sun can decide whether to dig them or not in order to get the highest score. The calculation rule of the total score is similar to the game Flappy 2048.

Dark_sun's bag is like a queue. When he gets a treasure, the treasure will be pushed back into the end of the bag. And the score will add the value of the treasure. For example, when there are treasures on the road in the order of {2, 8, 4, 8} and if Dark_sun decides to dig all of them, he will get the final score of 2+8+4+8=22. And his bag will finally become the sequence of {2, 8, 4, 8}.

If the adjacent treasures in the Dark_sun's bag have the same value, they will mix into a bigger treasure which has the value of their sum (the double value of the previous one). And Dark_sun will get a combo score of the value of bigger treasure. For example in the previous case, if Dark_sun decides to dig only the {2, 8, 8} treasure in sequence. He will get the basic score of 18(2+8+8). And when the last treasure (value 8) is pushed back into his bag, his bag will turn {2, 8, 8} into {2, 16} and he will get a bonus score of 16. And his final score will become 18+16=34 (which is the best strategy in this case.)

Notice that the treasures mix to the bigger one automatically when there are the same adjacent treasures. For example, when there are treasures of {2, 2, 4, 8, 16} on the road, and if Dark_sun decides to dig all of them, he will get the basic score of 32(2+2+4+8+16) and a bonus score of 60(4+8+16+32). At last he will get the total score of 92 and the bag becomes {32}.

Now, Dark_sun wants to get the highest score (no matter what's the treasure in his bag), can you tell him the what's the highest score?

Input

The first line is an integer n, which is the case number. In each case, the first line is an integer L, which is the length of the road.(0 < L ≤ 500) The second line contains L integers which can only be 2, 4, 8 or 16. This means the value of treasure on each area of the road.

Output

For each case, you should output an integer. The answer is the maximum of the total score which Dark_sun may get.

Sample Input

342 8 4 852 2 4 8 1688 4 4 2 8 4 2 2

Sample Output

3492116

Hint

In the third sample case, Dark_sun will choose {8,4,4,8,4,2,2}. Firstly, the first three treasures will be combined to 16 and then the {16,8,4,2,2} will become 32. And he will get the basic score 32(8+4+4+8+4+2+2) and the bonus score 84(8+16+4+8+16+32).


题目大意:给一个集合只有值为2,4,8,16的宝藏,现在给你n个宝藏和它们的价值,你可以从前往后放到背包中,相邻的价值相同的宝藏自动合成,对于每一个宝藏,你可以选择取或者不取,问如何使得总价值最大,总价值为宝藏的价值和合成的价值之和。


解题思路:原来还以为宝藏合成时要去选的,其实是自动合成的啊,跪了。

有一个性质:对于合成我们会保留32,16,8,4,2这样的递减串,那么在来一个2的话,就会变成64这一个数字了,而如果后面来一个8,那前面的状态就没有意义了,所以对于题目中500个16的极限情况我们只要保存12个数字就行(从4096到2),用二进制来表示每一位在前面有没有出现过,解法就很明显了,最后用滚动数组优化可以提高近10倍的效率。


#include <iostream>#include <cstdio>#include <cstring>#include <vector>using namespace std;int t,n;int a[501];int s;int dp[2][4097],f[4097];void init(){    for(int i=2,j=0;i<=4096;i*=2,j++) f[i]=j;}void read(){    scanf("%d",&n);    for(int i=1;i<=n;i++) scanf("%d",&a[i]);}void solve(){    memset(dp,-1,sizeof dp);    dp[0][0]=0;    int cur=0;    for(int i=1;i<=n;i++){        for(int j=0;j<=4096;j++) dp[1-cur][j]=dp[cur][j];        for(int j=0;j<=4096;j++){            if(dp[cur][j]==-1) continue;            int sum=0,now=a[i],tmp_j=j;            if(j&((1<<f[a[i]])-1)){                dp[1-cur][1<<f[a[i]]]=max(dp[1-cur][1<<f[a[i]]],dp[cur][j]+(j<<1));            }else{                int k;                for(k=f[a[i]];k<12;k++){                    if(tmp_j&(1<<k)){                        tmp_j&=~(1<<k);                        now<<=1;                        sum+=2*(1<<(k+1));                    }else break;                }                if(k<12) tmp_j|=(1<<k);                dp[1-cur][tmp_j]=max(dp[1-cur][tmp_j],dp[cur][j]+sum);            }        }        cur=1-cur;    }    int ret=0;    for(int i=0;i<=4096;i++){        if(dp[cur][i]==-1) continue;        ret=max(ret,dp[cur][i]+(i<<1));    }    printf("%d\n",ret);}int main(){    init();    scanf("%d",&t);    for(int ca=1;ca<=t;ca++){        read();        solve();    }    return 0;}



0 0