ZOJ -- 3802 Easy 2048 Again(状压dp)

给一个只含有2、4、8、16的序列,每个位置可以选或不选,必须从前到后选。选到一个数,就可以得到相应的分数。和2048规则相似,两个相邻的相同的数可以合并,并且变成它二倍的数。在这道题目中,合并一次也可以得到被合并数2倍的分数。(比如2 2序列,得到的分数就是2+2+4)

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5334

可以想到是dp,但是这道题的状态数太多了。每个字符都有选和不选,还要记录顺序。。。。但是

可以想到,一个在栈中的数要是想合并,那么它后边的数一定要比它小。比如现在栈中的数是8 4 2,那么再放入一个2就可以合并成16了。但是如果栈中的数是8 2 8,那么前边的8 2就无论如何都合并不了了。

所以我们只要记录当前状态包含哪些数字,数字一定是从大到小的,这样状态数就最多是16*500=8000(2^13=8196)。

dp[i][j]表示到达长度i,状态为j的最高分数。

这道题不用滚动数组会超时。。(初始化的时间太大了)

//#pragma comment(linker,"/STACK:1024000000,1024000000")
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

typedef double DB;
typedef long long ll;
typedef pair<int, int> PII;

#define pb push_back
#define MP make_pair
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
//#define Debug

const DB pi = acos(-1.0);
const DB eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const int maxn = 100000 + 10;

int lowbit(int x){
    return x & (-x);
}
int T, n, a[505], dp[2][1 << 13];
int main(){
#ifdef Debug
    freopen("in.txt", "r", stdin);
#endif // Debug
    scanf("%d", &T);
    while(T--){
        scanf("%d", &n);
        for(int i=1; i<=n; i++)
            scanf("%d", &a[i]);
        memset(dp, -1, sizeof(dp));
        dp[0][0] = 0;
        int now = 1;
        for(int i=1; i<=n; i++){
            for(int j=0; j<(1<<13); j++)if(dp[now ^ 1][j] != -1){
                int k = lowbit(j);
                dp[now][j] = max(dp[now][j], dp[now ^ 1][j]); ///不选
                if(a[i] > k) dp[now][a[i]] = max(dp[now][a[i]], dp[now ^ 1][j] + a[i]);
                else if(a[i] < k) dp[now][j | a[i]] = max(dp[now][j | a[i]], dp[now ^ 1][j] + a[i]);
                else{
                    int cur = j, res = dp[now ^ 1][j] + a[i], tmp = a[i];
                    while(lowbit(cur) == tmp){
                        tmp <<= 1;
                        res += tmp;
                        cur ^= lowbit(cur);
                    }
                    dp[now][cur | tmp] = max(dp[now][cur | tmp], res);
                }
            }
            now ^= 1;
        }
        int ans = 0;
        for(int i=0; i<(1<<13); i++)
            ans = max(ans, dp[now ^ 1][i]);
        printf("%d\n", ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值