P7458 [CERC2018] Clockwork ||ange 题解

Clockwork 题解

可将占用情况看为二进制数,兔子搬迁看作二进制数中的右移操作。

\(\text{Step1}\) 暴力

最简单粗暴的方法是 DFS,但由于时间复杂度过高并不可行。

\(\text{Step2}\) 优化

1. 顺序剪枝

根据贪心策略,若以同样的操作次数换来更多的 \(1\) ,那么后续相对其他操作则有更多次数去右移来填补 \(0\) 的位置,即总操作次数有更大的可能更小,所以我们将它放在较前的位置来搜索,为后续最优性剪枝服务。

2. 最优性剪枝

显然,若当前操作次数无论再怎么少都不可能比当前已搜索的答案更优的话,即可停止搜索,进行回溯。

\(\text{Step3}\) 代码

\(\text{Code}\)

#include <bits/stdc++.h>

using namespace std;

const int N = 50;

int n, ans = INT_MAX;
long long x;
char b[N];

template <typename type>
void read(type &res) {
    type x = 0, f = 1;
    char c = getchar();
    for (; c < 48 || c > 57; c = getchar()) if (c == '-') f = ~f + 1;
    for (; c > 47 && c < 58; c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
    res = f * x;
}

// __builtin_popcountll(x) 为查询 x 在二进制状态下有多少个 1 的函数
void dfs(long long x, int step) {
	if (__builtin_popcountll(x) == n) return void(ans = min(ans, step)); // 更新答案
    long long maxx = x;
	int maxc = __builtin_popcountll(x);
    // 1. 顺序剪枝 先考虑答案可能小的部分
	for (int k = 1; k <= n; k++) {
		long long nx = x >> k;
		if (__builtin_popcountll(nx | x) > maxc) {
			maxc = __builtin_popcountll(nx | x);
			maxx = nx | x;
		}
	}
    // 2. 最优性剪枝
	if (step + 1 < ans) dfs(maxx, step + 1);
    // 1. 顺序剪枝 再考虑剩下部分
	for (int k = 1; k <= n; k++) {
		long long nx = x >> k;
		if (nx == maxx && __builtin_popcountll(nx | x) == maxc) continue;
        // 2. 最优性剪枝
		if (__builtin_popcountll(nx | x) != maxc && step + 1 < ans) dfs(nx | x, step + 1);
	}
}

int main() {
	scanf("%s", b);
	n = strlen(b);
    // 字符串到数的转化
	for (int i = 0; i < n; i++) 
		if (b[i] == '0' && !x) return puts("-1"), 0;
		else if (b[i] == '1') x = x + (1ll << (n - 1 - i));
	dfs(x, 0); // 搜索 + 剪枝
	printf("%d", ans);
    return 0;
}
posted @ 2025-05-10 14:35  ZaleClover  阅读(22)  评论(0)    收藏  举报