zoj 3802 Easy 2048 Again (14.8 浙大月赛 E)

        题意:就像那个游戏2048一样。。不过是一维版的。有n个数,只有可能是2,4,8,16,两个相同的数合并可以double并得到 等于它们的和 的额外积分。问最多拿到多少分。

        思路:状压dp。二进制表示集合,集合的内容是,当前栈中“可能会被”合并掉的数。首先有个很重要的结论,就是栈中如果有可以被合并的n,那么不会有多于1个n可以被合并。比如当前栈是 x 8 x x 8,假设这两个8都可以被合并,那么右边的8(靠近栈顶)会先被合并,得到16,这样的话,不加入新的数的情况下,这个栈就不会产生小于16的数了,也就是被16堵住了,前面小于16的数统统不能被合并。因此,可以用一个二进制数来表示这个栈中可能被合并的数,当然不能被合并的那些数我们不关心。

        有了这样的想法,就可以用一个滚动数组来dp了,数组开8192,因为n最大是500,512*16=8192。要注意对滚动数组的更新。


#include <iostream>       
#include <stdio.h>       
#include <cmath>       
#include <algorithm>       
#include <iomanip>       
#include <cstdlib>       
#include <string>       
#include <memory.h>       
#include <vector>       
#include <queue>       
#include <stack>       
#include <map>     
#include <set>     
#include <ctype.h>       
#define INF 1000000     
#define ll long long   
#define min3(a,b,c) min(a,min(b,c))   
    
using namespace std;    

int num[512];

int dp[8192][2];

int main(){
    int t;
    cin>>t;
    int n;
    while(t--){
        cin>>n;
        int ans=0;
        memset(dp,-1,sizeof(dp));
        for(int i=1;i<=n;i++){
            cin>>num[i];
        }
        dp[0][0]=dp[0][1]=0;
        
        for(int i=1;i<=n;i++){
            for(int j=0;j<8192;j++){
                if(dp[j][!(i&1)]==-1)continue;
                //更新 
                dp[j][i&1]=max(dp[j][i&1],dp[j][!(i&1)]);
                
                int cur=num[i];
                //t描述了新栈的情况 
                int t;
                if(j&(cur-1)){
                    t=cur;//堵住了比当前数小的数 
                }else{
                    t=j+cur;
                }
                //s是得到的额外积分 
                int s=0;
                int p=j;
                
                if( !(j&(cur-1)) )while(p&cur){
                    p=p-cur;
                    cur<<=1;
                    s+=cur;
                }
                //原积分+额外积分+当前拿到的数 
                if(dp[j][!(i&1)]+s+num[i]>dp[t][i&1]){
                    dp[t][i&1]=dp[j][!(i&1)]+s+num[i];
                }
                
                ans=max(dp[t][i&1],ans);
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值