zoukankan      html  css  js  c++  java
  • 【CERC 2014 E】2048

    题意

      2048曾经是一款风靡全球的小游戏。
      今天,我们换一种方式来玩这个小游戏。
      你有一个双端队列,你只能把元素从左端或从右端放入双端队列中。一旦放入就不得取出。放入后,若队列中有连续两个相同的元素,它们将自动合并变成一个新的元素——原来那两个元素的和。若新的元素与它相邻的元素相同,则继续合并……
      如:双端队列中有2, 4, 16三个元素。若将2从左端插入双端队列中,该队列将变成8, 16。若将2从右端插入双端队列中,该队列将变成2, 4, 16, 2。
      一开始,双端队列为空。我们将给你一些数,你需要依次插入到双端队列中。问是否存在一种操作方案,使得双端队列最后只剩下一个数。
      (1le nle 1000,space sumlimits_{i=1}^{n}a_ile 2^{13},space Tle 10000),其中 (ngt 20) 的数据不超过 (150) 组。

    题解

      小学生手玩 (1s) 可得:如果一个数被夹在两个大于它的数中间,最后队列里就至少剩下 (3) 个数。
      也就是说,任何时刻队列一定是单峰的,峰左边的数单调递增,峰右边的数单调递减。
      所以直接贪心,判断新加入的数是否 (le) 队列左端的数,是则把新数加到队列左端;否则判断新加入的数是否 (le) 队列右端的数,是则把新数加到队列右端;否则该局面没救了,回溯改之前的某些两可情况(即之前加入某个数时,这个数同时 (le) 队列两端的数,可以加到任意一端。你可能只尝试加到了一端,现在回去改加到另一端)。

      观察 (a_i),发现不仅都是 (2^k),而且总和 (le 2^{13}),那是不是随便二进制状压一下两边的数,然后记忆化搜索一下就行了?
      状压显然可行,因为每个数在每一边的出现次数都是 (0)(1),如果出现了 (2) 次,由于数列单调,这两个数相邻,所以会拼成一个更大的数。而每个数直接对应一个二进制位,拼两个数根本不用任何特殊操作,直接加上新来的数就自动进位了。
      然而因为有 (1w) 组数据,复杂度貌似不太支持把两边都状压。
      考虑可不可以只状压左边,把右边用左边的状态表示出来。不难发现由于是依次加入数,我们每次加入一个数后都知道所有加入的数之和,用总和减去左边的数之和 就是右边的数之和了。

    #include<bits/stdc++.h>
    #define N 1010
    #define M 8195
    #define R sum[x]-L
    using namespace std;
    inline int read(){
        int x=0; bool f=1; char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
        for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
        if(f) return x; return 0-x;
    }
    int t,T,n,highbit[M],a[N],sum[N],vis[N][M]; char ans[N]; bool flag;
    inline int lowbit(int x){return x&-x;}
    void dfs(int x, int L){
        if(highbit[R] >= highbit[L]) L+=highbit[R];
        if(vis[x][L]==T) return;
        vis[x][L]=T;
        if(x==n){
            if(L==sum[x] && L==lowbit(L)) flag=1; //两条判断是等价的,可以只取其一 
            return;
        }
        int y=x+1, l=lowbit(L), r=lowbit(R);
        if(a[y]<=l) ans[y]='l', dfs(y,L+a[y]);
        if(flag) return;
        if(!r || a[y]<=r) ans[y]='r', dfs(y,L);
    }
    int main(){
        t=read();
        for(int i=2; i<M; ++i) highbit[i]=highbit[i>>1]+1;
        for(int i=1; i<M; ++i) highbit[i]=1<<highbit[i];
        for(T=1; T<=t; ++T){
            n=read();
            for(int i=1; i<=n; ++i) a[i]=read(), sum[i]=sum[i-1]+a[i];
            flag=0;
            ans[1]='l', dfs(1,a[1]);
            if(!flag) printf("no
    ");
            else{ans[n+1]=0; printf("%s
    ",ans+1);}
        }
        return 0;
    }
    
  • 相关阅读:
    pandas 之 数据合并
    第一册:lesson sixty five.
    第一册:lesson sixty three。
    C#正则表达式。
    第一册:lesson sixty one.
    C#泛型。
    SQL命令入门。
    C#序列化与反序列化。
    第一册:lesson fifty nine。
    C#文件操作。
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/cerc2014e.html
Copyright © 2011-2022 走看看