【CERC 2014 E】2048

题意

  2048曾经是一款风靡全球的小游戏。
  今天,我们换一种方式来玩这个小游戏。
  你有一个双端队列,你只能把元素从左端或从右端放入双端队列中。一旦放入就不得取出。放入后,若队列中有连续两个相同的元素,它们将自动合并变成一个新的元素——原来那两个元素的和。若新的元素与它相邻的元素相同,则继续合并……
  如:双端队列中有2, 4, 16三个元素。若将2从左端插入双端队列中,该队列将变成8, 16。若将2从右端插入双端队列中,该队列将变成2, 4, 16, 2。
  一开始,双端队列为空。我们将给你一些数,你需要依次插入到双端队列中。问是否存在一种操作方案,使得双端队列最后只剩下一个数。
  \(1\le n\le 1000,\space \sum\limits_{i=1}^{n}a_i\le 2^{13},\space T\le 10000\),其中 \(n\gt 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\n");
        else{ans[n+1]=0; printf("%s\n",ans+1);}
    }
    return 0;
}

转载于:https://www.cnblogs.com/scx2015noip-as-php/p/cerc2014e.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值