Uvalive6931 CERC2014 E - Can't stop playing(搜索)

https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4943

题意:一维2048。对于当前序列,只能把当前的数字放在最左边或者最右边,相邻两个数字如果相同会融合。问能否通过贪心操作使得最后序列只有一个元素。

思路:

首先,要构造的序列一定是一个’ ^ ‘形的,因为一旦是’ v ‘形,处于低谷的那个小数字永远不可能被消除。

那么可以把构造出来的序列拆分成左右两部分,使得左半部分呈上升趋势,右半部分呈下降趋势。

由于所有值都是二的倍数,左半部分可以用一个数字直接表示出来。

比如: 左序列 1 2 4 8 可以直接用数字15表示,而数字15能且只能表示序列1 2 4 8 。

这里只讨论左半部分,右半部分其实是一样的,不再赘述。

那么,采取搜索的方式,假设选到第pos个数字时,左半序列和为lsum;

对于第pos个数字(假设为a[pos]),如果a[pos]想插在左边

(1)满足:a[pos]不大于左半序列的最小数字。

所以我们求出lsum的lowbit值,就可以知道a[pos]可不可以放在左半序列。(注意这里,如果lowbit为0,应该改为把0改为inf)

如果a[pos]可以放在左边,我们继续记忆化搜索:dfs(pos+1,lsum+a[pos]);

(2)满需:左半序列可以直接和右半序列合并,相当于把左半序列移到右边去;

如果能把左边的序列全都移到右边去,我们也能继续搜索。

至于highBit,意义和lowbit是相反的,用于记录lsum最大的那个数字是多少。

右边同理。

#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;

#define maxn 1024
char ans[maxn];
int n;
int sum[maxn];
bool vis[maxn][(1<<13)+5];
int highbit[1<<14];
int a[1024];

int getlowbit(int val){
    for(int i=0;i<14;i++){
        if(val&(1<<i))
            return (1<<i);
    }return (1<<14);
}

bool dfs(int pos,int lsum){
    bool ret=0;
    int rsum = sum[pos-1]-lsum;
    if(pos>n){
        if(lsum==highbit[lsum]&&rsum==highbit[rsum]&&lsum+rsum==highbit[lsum+rsum]){
            return true;
        }return false;
    }
    if(vis[pos][lsum]||vis[pos][rsum])return false;
    vis[pos][lsum]=vis[pos][rsum]=true;

    int lowbitL = getlowbit(lsum);
    int lowbitR = getlowbit(rsum);

    if(a[pos]<=lowbitL){
        int L=lsum + a[pos], R=rsum;
        if(highbit[L]==highbit[R]){
            L+=highbit[L];
            R-=highbit[R];
        }
        if(dfs(pos+1,L)){
            ans[pos-1]='l';ret=1;
        }
    }
    else if(rsum==highbit[rsum]&&rsum>=highbit[lsum]){
        int L=lsum+rsum, R=a[pos];
        if(highbit[L]==highbit[R]){
            L+=highbit[L];
            R-=highbit[R];
        }
        if(dfs(pos+1,L)){
            ans[pos-1]='r';ret=1;
        }
    }
    if(a[pos]<=lowbitR){
        int L=lsum, R=rsum + a[pos];
        if(highbit[L]==highbit[R]){
            L+=highbit[L];
            R-=highbit[R];
        }
        if(dfs(pos+1,L)){
            ans[pos-1]='r';ret=1;
        }
    }
    else if(lsum==highbit[lsum]&&lsum>=highbit[rsum]){
        int L=a[pos], R=lsum+rsum;
        if(highbit[L]==highbit[R]){
            L+=highbit[L];
            R-=highbit[R];
        }
        if(dfs(pos+1,L)){
            ans[pos-1]='l';ret=1;
        }
    }
    return ret;
}

int main(){
    for(int i=1;i<(1<<14);i++){
        if((i&-i)==i)highbit[i]=i;
        else highbit[i]=highbit[i-1];
    }
    int t;cin>>t;
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",a+i);
            sum[i]=sum[i-1]+a[i];
            memset(vis[i],0,sizeof(vis[i]));
        }
        if(dfs(1,0)){
            ans[n]='\0';
            printf("%s\n",ans);
        }
        else{
            puts("no");
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值