UVALive 6931 Can't stop playing (Regionals 2014 >> Europe - Central)

本文探讨了一种特殊的队列合并问题,即在一个双头队列中添加若干个二的幂次方数值,并允许相邻相同的数合并。通过巧妙的状态记录方法,实现了一种搜索算法来判断最终队列是否能仅剩下一个数。

题目

一开始有一个双头队列,每次添加一个数(这是数是二的幂,所有数的和不大于\(2^13\)),由你来决定添加到队头还是队尾。如果队列里面相邻的两个数相同,设它们都是\(x\),那么这两个数会合并为\(2x\)。问所有数添加完后队列里能否只剩下一个数。

算法

搜索题,但是需要巧妙地记录状态!这种题不可多。

一个显然的是,队列里不会存在相邻的三个数\(a,b,c\),满足\(a>b,c>b\)。这样的话,队列肯定是一个倒V字。

记状态\((i,j)\)为添加完前\(i\)个数,\(j\)是倒V左半边的上升序列状态,\(j\)最大是\(2^{13}\)。通过这两个,我们可以算出倒V右半边的序列状态!

这样的话,时间复杂度就是\(O(n 2^{13})\)

代码

官方的标程有点点小错误。UVALive上的数据也错了。但当时比赛的数据是正确的!

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <vector>
#include <assert.h>
using namespace std;

template <class T>
void tension(T &a, const T &b) {
    if (b < a) a = b;
}

template <class T>
void relax(T &a, const T &b) {
    if (b > a) a = b;
}

typedef long long i64;

#ifndef ONLINE_JUDGE
#define ep(...) fprintf(stderr, __VA_ARGS__)
#else
#define ep(...) assert(true)
#endif

int n;
int sum[1003], A[1003];
bool vis[1003][(1 << 13) + 1];
char ans[1003];
int highBit[1 << 14];

int place(int a, int b, int x) {
    if (highBit[a] > highBit[b]) {
        int tmp = highBit[a];
        a -= tmp;
        b += tmp;
    }
    if (a && (a & -a) < x) return -1;
    a += x;
    while (highBit[a] == highBit[b]) {
        int tmp = highBit[a];
        a += tmp;
        b -= tmp;
    }
    return a;
}

bool dfs(int k, int s1) {
    int s2 = (k ? sum[k - 1] : 0) - s1;
    
    if (k == n) {
        if (highBit[s1] > highBit[s2]) {
            int tmp = highBit[s1];
            s1 -= tmp;
            s2 += tmp;
        }
        if (highBit[s2] > highBit[s1]) {
            int tmp = highBit[s2];
            s2 -= tmp;
            s1 += tmp;
        }
        if (s1 == (s1 & -s1) && s2 == 0) return true;
        if (s2 == (s2 & -s2) && s1 == 0) return true;
        return false;
    }
    
    if (vis[k][s1]) return false;
    vis[k][s1] = true;
    
    int x = A[k];
    int tmp = place(s1, s2, x);
    if (tmp != -1 && dfs(k + 1, tmp)) {
        //ep("l (%d, %d) %d ->\n", s1, s2, x);
        ans[k] = 'l';
        return true;
    }
    tmp = place(s2, s1, x);
    if (tmp != -1 && dfs(k + 1, sum[k] - tmp)) {
        //ep("r (%d, %d) %d ->\n", s1, s2, x);
        ans[k] = 'r';
        return true;
    }
    return false;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt", "r", stdin);
    //freopen("all.in", "r", stdin);
    freopen("output.txt", "w", stdout);
#endif
    for (int i = 1; i < 1 << 14; i ++) 
        if ((i & -i) == i) highBit[i] = i;
        else highBit[i] = highBit[i - 1];

    scanf("%*d");
    for (int cases = 1; scanf("%d", &n) == 1; cases ++) {
        //ep("Case %d: ", cases);
        for (int i = 0; i < n; i ++) {
            scanf("%d", A + i);
            sum[i] = A[i];
            if (i) sum[i] += sum[i - 1];
        }
        
        //memset(vis, 0, sizeof vis);
        for (int i = 0; i < n; i ++)
            memset(vis[i], 0, sizeof vis[i]);
        if (dfs(0, 0)) {
            ans[n] = '\0';
            printf("%s\n", ans);
        }
        else printf("no\n");
    }

    return 0;
}

转载于:https://www.cnblogs.com/wangck/p/4528905.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值