解题思路
卡着时间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;
}