zoj3802:easy 2048 again(状压dp)

zoj月赛的题目,非常不错的一个状压dp。。

题目大意是一个一维的2048游戏

只要有相邻的相同就会合并,合并之后会有奖励分数,总共n个,每个都可以取或者不取

问最终得到的最大值

数据范围n<=500 , a[i]={2,4,8,16};

分析:

首先明确一下自动合并的意思,比如原有 8,4,2,进入一个2 就会变成16

所以我们需要记录前面的所有数字。。计算了一下发现最大情况,500个16会合成4096 =2^12

显然全部记录是不可能的。那么怎么处理呢

我们发现,只有递减的序列才有可能向前合并。。所以我们只需要记录某个状态末尾的递减序列即可

最大数只有2^12,所以递减序列个数只有2^13-1种,可以记录了。。

之后就是状态转移的问题了。

不取当前数状态不变

取当前数分三种情况

1.前面有比当前数更小的,则如果取这个数,递减序列将只有这一个数

2.前面的末尾恰好跟当前数相等,那么向上合并直至不能合并为止 

3.前面的末尾比当前数大,那么直接将当前数插入状态中

具体实现看代码,用了一点位运算挺有意思的

#include <iostream>
#include <stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<ctype.h>
using namespace std;
#define MAXN 10000
int dp[2][8200];
int a[505];
int main()
{
    #ifndef ONLINE_JUDGE
        //freopen("in.txt","r",stdin);
    #endif
    int T,n;
    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[1][0]=0;
        dp[1][a[1]]=a[1];
        for(int i=2;i<=n;i++)
        {
            for(int j=0;j<=8191;j++)
            {
                if(dp[(i-1)%2][j]==-1)
                {
                    continue;
                }
                dp[i%2][j]=max(dp[i%2][j],dp[(i-1)%2][j]);           //不取
                if(j&(a[i]-1))
                {
                    dp[i%2][a[i]]=max(dp[i%2][a[i]],dp[(i-1)%2][j]+a[i]);     //情况1
                    continue;    
                }
                int state,score;
                if(j&a[i])
                {
                    int tmp=j/a[i],k=0;
                    score=a[i];
                    while(tmp%2)
                    {
                        k++;
                        tmp/=2;
                        score+=a[i]<<k;
                    }
                    state=((tmp<<k)*a[i])|(a[i]<<k);
                    dp[i%2][state]=max(dp[i%2][state],dp[(i-1)%2][j]+score);  //情况2
                    continue;
                }
                state=j|a[i];
                score=a[i];
                dp[i%2][state]=max(dp[i%2][state],dp[(i-1)%2][j]+score);      //情况3
            }
        }
        int ans=-1;
        for(int i=0;i<8192;i++)
        {
            ans=max(ans,dp[n%2][i]);
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

posted @ 2014-10-31 19:59  PlasticSpirit  阅读(312)  评论(0编辑  收藏  举报