[刷题] 搜索剪枝技巧
https://wenku.baidu.com/view/83b35f22aaea998fcc220e6a.html
深度优先搜索问题的优化技巧
提及:
ZOJ1937
IOI2000 BLOCK
NOI2005 智慧珠
USACO weight
提交通道:weight
Description
已知原数列\(a_1,a_2,\dots,a_n\)中的前\(1\)项,前\(2\)项,前\(3\)项,\(\dots\),前\(n\)项的和,以及后\(1\)项,后\(2\)项,后\(3\)项,\(\dots\),后\(n\)项的和,但是所有的数都被打乱了顺序。
此外,我们还知道数列中的数存在于集合\(S\)中。试求原数列。
当存在多组可能的数列时,求字典序最小的数列。
数据范围 \(1\le n\le1000,1\le m\le500\) ,且 \(S \in\{1,2,\dots,500\}\) 。
Solution
详情见文章和代码。
Code
// Author: wlzhouzhuan
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define rint register int
#define rep(i, l, r) for (rint i = l; i <= r; i++)
#define per(i, l, r) for (rint i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
inline int read() {
int x = 0, neg = 1; char op = getchar();
while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
return neg * x;
}
inline void print(int x) {
if (x < 0) { putchar('-'); x = -x; }
if (x >= 10) print(x / 10);
putchar(x % 10 + '0');
}
const int N = 20000005;
int can[N], s[N], n, m;
int a[N], b[N], lsum[N], rsum[N];
void dfs(int k, int i, int j) { // 到s[k],已经确定a[1],a[2]...a[i]和a[j],a[j+1]....a[n]
if (i + 1 == j) {
for (int p = i; p >= 1; p--) b[p] = a[p] - a[p - 1];
for (int p = j; p <= n; p++) b[p] = a[p] - a[p + 1];
for (int p = 1; p <= n; p++) lsum[p] = lsum[p - 1] + b[p];
for (int p = n; p >= 1; p--) rsum[p] = rsum[p + 1] + b[p];
/*printf("preout = \n");
for (int p = 1; p <= n; p++) {
printf("%d ", b[p]);
}
puts("");
*/
int le = n, ri = 1, tp = 2 * n;
while (tp > n) {
if (le > i && lsum[le] == s[tp]) le--;
else if (ri < j && rsum[ri] == s[tp]) ri++;
else return ;
tp--;
}
for (int p = 1; p <= n; p++) {
printf("%d ", b[p]);
}
puts("");
exit(0);
}
if (k >= n + 1) {
return ;
}
if (s[k] - a[i] > 0 && s[k] - a[i] <= 500 && can[s[k] - a[i]]) {
a[i + 1] = s[k];
dfs(k + 1, i + 1, j);
a[i + 1] = 0;
}
if (s[k] - a[j] > 0 && s[k] - a[j] <= 500 && can[s[k] - a[j]]) {
a[j - 1] = s[k];
dfs(k + 1, i, j - 1);
a[j - 1] = 0;
}
}
int main() {
n = read();
for (int i = 1; i <= 2 * n; i++) s[i] = read();
sort(s + 1, s + 2 * n + 1);
m = read();
while (m--) {
int x = read();
can[x] = 1;
}
dfs(1, 0, n + 1);
return 0;
}