[刷题] 搜索剪枝技巧

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; 
}
posted @ 2020-04-21 21:56  wlzhouzhuan  阅读(379)  评论(0编辑  收藏  举报