hdu 5440 Clock Adjusting(双向bfs)

223 篇文章 1 订阅
179 篇文章 0 订阅

题目链接:hdu 5440 Clock Adjusting

解题思路

卡着时间AC的,双向bfs,根据指向1,2…,12的clock个数建立hash函数,从终止状态逆向搜索5步,从起始状态正向搜索3步(因为终止状态对应的都是12,所以转移起来状态相对起始状态要少一些)

使用tool的顺序是无关紧要的,所以在搜索时,考虑到状态s,是通过使用前i个tool转移过来的,那么只需要在考虑使用i,i+1…,M即可(这里我优先使用编号大的tool,不确定有没有优化的作用)

在确定状态s和工具i后,需要枚举修改哪些clock,一开始我是用二进制状态表示需要被修改的地方,但是重复的太多,就用dfs+剪枝枚举。

这题应该还有更厉害的优化,我是卡1400Ms过的。

代码

#include <cstdio>
#include <cstring>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;
typedef long long ll;
const int maxn = 1 * 1e5 + 5;

ll S;
int N, M, L[10], X[10], head, rear, ans;
map<ll, int> C;
set<ll> T;

struct State {
    ll s;
    int f, c;
    State(ll s = 0, int f = 0, int c = 0): s(s), f(f), c(c) {}
}Q[maxn], cur;

inline ll idx(int* c) {
    ll ret = 0;
    for (int i = 0; i < 12; i++)
        ret = ret * (N+1) + c[i];
    return ret;
}

inline void reidx(ll s, int* c) {
    for (int i = 11; i >= 0; i--) {
        c[i] = s % (N+1);
        s /= (N+1);
    }
}

void init () {
    C.clear();
    T.clear();
    scanf("%d%d", &N, &M);

    int x, cnt[15];
    memset(cnt, 0, sizeof(cnt));
    for (int i = 1; i <= N; i++) {
        scanf("%d", &x);
        if (x == 12) x = 0;
        cnt[x]++;
    }
    S = idx(cnt);

    for (int i = 0; i < M; i++)
        scanf("%d%d", &L[i], &X[i]);
}

bool dfs1 (int d, int* c, int* t, int n, int m, int v) {
    if (d > 12 || n > m) return false;

    if (n == 0 && m == 0) {
        ll tmp = idx(t);
        if (tmp == S) {
            ans = cur.c + 1;
            return true;
        }
        if (!C.count(tmp)) {
            C[tmp] = cur.c + 1;
            if (cur.c < 4) Q[rear++] = State(tmp, v, cur.c+1);
        }
        return false;
    }

    for (int i = min(c[d], n); i >= 0; i--) {
        if (n-i > m-c[d]) break;
        t[(d-X[v]+12)%12] += i;
        t[d] += (c[d] - i);
        if (dfs1(d+1, c, t, n-i, m-c[d], v)) return true;
        t[(d-X[v]+12)%12] -= i;
        t[d] -= (c[d] - i);
    }
    return false;
}

bool dfs2 (int d, int* c, int* t, int n, int m, int v) {
    if (d > 12 || n > m) return false;

    if (n == 0 && m == 0) {
        ll tmp = idx(t);
        if (C.count(tmp)) {
            ans = C[tmp] + cur.c + 1;
            return true;
        }
        if (!T.count(tmp) && cur.c < 2) {
            T.insert(tmp);
            Q[rear++] = State(tmp, v, cur.c+1);
        }
        return false;
    }

    for (int i = min(c[d], n); i >= 0; i--) {
        if (n-i > m-c[d]) break;
        t[(d+X[v]+12)%12] += i;
        t[d] += (c[d] - i);
        if (dfs2(d+1, c, t, n-i, m-c[d], v)) return true;
        t[(d+X[v]+12)%12] -= i;
        t[d] -= (c[d] - i);
    }
    return false;
}

int bfs() {
    int cnt[15], pos[15];
    memset(cnt, 0, sizeof(cnt));
    cnt[0] = N;
    ll k = idx(cnt);
    if (S == k) return 0;
    C[k] = 0;


    head = rear = 0;
    for (int i = 0; i < M; i++) {
        int x = (-X[i] + 12) % 12;
        cnt[x] += L[i];
        cnt[0] -= L[i];

        ll tmp = idx(cnt);
        if (!C.count(tmp)) {
            if (tmp == S) return 1;
            Q[rear++] = State(tmp, i, 1);
            C[tmp] = 1;
        }

        cnt[x] -= L[i];
        cnt[0] += L[i];
    }

    while (head < rear) {
        cur = Q[head++];
        reidx(cur.s, cnt);

        for (int i = M-1; i >= cur.f; i--) {
            memset(pos, 0, sizeof(pos));
            if (dfs1(0, cnt, pos, L[i], N, i))
                return ans;
        }
    }

    head = rear = 0;
    Q[rear++] = State(S, 0, 0);

    while (head < rear) {
        cur = Q[head++];
        reidx(cur.s, cnt);

        for (int i = M-1; i >= cur.f; i--) {
            memset(pos, 0, sizeof(pos));
            if (dfs2(0, cnt, pos, L[i], N, i)) return ans;
        }
    }
    return -1;
}

int main () {
    int cas;
    scanf("%d", &cas);
    while (cas--) {
        init();
        printf("%d\n", bfs());
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值