zoukankan      html  css  js  c++  java
  • 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
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    零基础学python-16.7 nonlocal介绍
    零基础学python-16.6 嵌套作用域
    零基础学python-16.5 模块间全局变量的修改以及其他访问全局变量的方法
    零基础学python-16.4 global作用域
    零基础学python-16.3 变量名解析:LEGB原则
    如何格式化日期和时间
    如何创建指定的日期和时间
    如何遍历Set对象
    如何遍历Map对象
    如何遍历List对象
  • 原文地址:https://www.cnblogs.com/oneshot/p/4065859.html
Copyright © 2011-2022 走看看