深搜的剪枝技巧

剪枝方法

1.優化搜索順序

在不同的問題中,搜索樹的各個層次、各個分支之間的順序不是固定的,不同的搜索順序會產生不同的搜索樹形態,其規模大小也相差甚遠。

2.排除等效冗餘

在搜索過程中,若能判斷從搜索樹當前節點上沿某幾條不同分支到達的子樹是相同的,那麼只需對其中一條分支執行搜索。

3.可行性剪枝

可行性剪枝也叫上下界剪枝,其是指在搜索過程中,及時對當前狀態進行檢查,若發現分支已無法到達遞歸邊界,就執行回溯。

4.最優性剪枝

在最優化問題的搜索過程中,若當前花費的代價已超過當前搜索到的最優解,那麼無論採取多麼優秀的策略到達遞歸邊界,都不可能更新答案,此時可以停止對當前分支的搜索進行回溯。

題目

psps:懶得放題解了,湊合看吧,zzz

LOJ #10018. 「一本通 1.3 例 1」數的劃分

題目鏈接

題意:將整數nn分成kk份,且每份不能爲空,問有多少種不同的分法

直接搜會TT,要想剪枝:

  1. 每次搜索都從上一個搜到的數開始搜,去重
  2. 剩餘未被劃分的數不能小於剩餘劃分的次數

借大佬說的話:

因爲保證沒有重複方案,就必須保證枚舉的結果遞增(或者遞減)。假設當前選擇的數爲xx,那麼後面所有的數必須x≥x,如果此時讓剩下的數都=x=x,還比nn小,那麼剩下的幾次遞歸其實都是無用的,這就是這一題的可行性剪枝。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

inline int read() {
	char c = getchar();
	int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
	return x * f;
}

int n, k, ans;

void dfs(int last, int cnt, int sum) {
	if(sum > n) return;
	if(cnt == k) {
		if(sum == n) ans++;
		return;
	}
	for(int i = last; sum + i * (k - cnt) <= n; i++) {
		dfs(i, cnt + 1, sum + i);
	}
}

int main() {
	n = read(), k = read();
	dfs(1, 0, 0);
	cout << ans << '\n';
	return 0;
}

LOJ #10019. 「一本通 1.3 例 2」生日蛋糕

題目鏈接

#include <cmath> 
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int A = 30;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar(); int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

int n, m;
int minv[A], mins[A], ans = inf, h[A], r[A], s = 0, v = 0;

void dfs(int dep) {
	if(!dep) {
		if(v == n) ans = min(ans, s);
		return;
	}
	for(r[dep] = min((int)sqrt(n - v), r[dep + 1] - 1); r[dep] >= dep; r[dep]--)
		for(h[dep] = min((int)((double)(n-v) / r[dep] / r[dep]), h[dep + 1] - 1); h[dep] >= dep; h[dep]--) {
			if (v + minv[dep - 1] > n) continue;
			if (s + mins[dep - 1] > ans) continue;
			if (s + (double)2 * (n - v) / r[dep] > ans) continue;
			if(dep == m) s += r[dep] * r[dep];
			s += 2 * r[dep] * h[dep];
			v += r[dep] * r[dep] * h[dep];
			dfs(dep - 1);
			if (dep == m) s -= r[dep] * r[dep];
			s -= 2 * r[dep] * h[dep];
			v -= r[dep] * r[dep] * h[dep];
		}
}

int main() {
	n = read(), m = read();
	minv[0] = mins[0] = 0;
	for(int i = 1; i <= m; i++) {
		minv[i] = minv[i - 1] + i * i * i;
		mins[i] = mins[i - 1] + i * i;
	}
	h[m + 1] = r[m + 1] = inf;
	dfs(m);
	cout << ans << "\n";
	return 0;
}

LOJ #10020. 「一本通 1.3 例 3」小木棍

題目鏈接

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

inline int read() {
	char c = getchar();
	int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
	return x * f;
}

const int N = 101;

int n, a[N], sum, len, maxn, vis[N], cnt, ll, tot;


bool dfs(int now, int lo, int last) {
	if(now == tot + 1 && lo == len) return true;
	if(lo == len) lo = 0, last = 0;
	for(int i = last + 1; i <= tot; i++) {
		if(vis[i]) continue;
		if(a[i] + lo > len) continue;
		vis[i] = 1;
		if(dfs(now + 1, lo + a[i], i)) return true;
		vis[i] = 0;
		if(lo == 0 || lo + a[i] == len) return false;
		while(a[i] == a[i + 1]) i++;
	}
	return false;
}

bool cmp(int a, int b) {
	return a > b;
}

int main() {
	n = read();
	sum = 0, maxn = 0;
	for(int i = 1; i <= n; i++) {
		int x = read();
		if(x <= 50) {
			a[++tot] = x;
			maxn = max(maxn, a[tot]);
			sum += a[tot];
		}
	}
	stable_sort(a + 1, a + 1 + tot, cmp);
//	cout << sum << '\n';
//	for(int i = 1; i <= tot; i++) cout << a[i] << ' '; 
	for(len = maxn; len <= sum; len++) {
		if(sum % len) continue;
		if(dfs(1, 0, 0)) {
			cout << len;
			return 0;
		}
	}
	cout << sum << '\n';
	return 0;
}

LOJ #10021. 「一本通 1.3 例 4」Addition Chains

題目鏈接

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int A = 5e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar(); int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

int a[A], ans[A], minn, n;

void dfs(int now) {
	if(now > minn || a[now] > n) return;
	if(a[now] == n) {
		if(now > minn) return;
		minn = now;
		for(int i = 1; i <= now; i++) ans[i] = a[i];
		return;
	}
	for(int i = now; i >= 1; i--) {
		if(a[now] + a[i] > n) continue;
		a[now + 1] = a[now] + a[i];
		dfs(now + 1);
		a[now + 1] = 0;
	}
	return;
}

int main() {
	while(scanf("%d", &n) != EOF) {
		if(n == 0) break;
		minn = inf, a[1] = 1;
		dfs(1);
		for(int i = 1; i <= minn; i++) cout << ans[i] << " ";
		puts("");
	}
	return 0;
}

LOJ #10249. 「一本通 1.3 例 5」weight

題目鏈接

神奇的搜索對象

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int A = 5e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar(); int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

int x, n, m, p, sum[A], a[A];
bool vis[A];

bool dfs(int cnt, int l, int r, int lsum, int rsum) {
	if(l == r) {
		if(!vis[sum[cnt] - lsum] && !vis[sum[cnt] - rsum]) return 0;
		a[l] = sum[n * 2] - lsum - rsum;
		if(a[l] < 1 || a[l] > 500) return 0;
		for(int i = 1; i <= cnt; i++) cout << a[i] << " ";
		return 1;
	}
	if(vis[sum[cnt] - lsum]) {
		a[l] = sum[cnt] - lsum;
		if(dfs(cnt + 1, l + 1, r, sum[cnt], rsum)) return 1;
	}
	if(vis[sum[cnt] - rsum]) {
		a[r] = sum[cnt] - rsum;
		if(dfs(cnt + 1, l, r - 1, lsum, sum[cnt])) return 1;
	}
	return 0;
}

int main() {
	n = read();
	for(int i = 1; i <= 2 * n; i++) sum[i] = read();
	m = read();
	for(int i = 1; i <= m; i++) x = read(), vis[x] = 1;
	stable_sort(sum + 1, sum + 1 + n * 2);
	dfs(1, 1, n, 0, 0);
}

LOJ #10022. 「一本通 1.3 練習 1」埃及分數

題目鏈接

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;

const int A = 5e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar();
	int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

int n, m, cur[1005], ans[1005], mn;

void dfs(int x, int a, int b, int n, int last) {
	if (x == n) {
		if(b % a) return;
		b /= a; cur[x] = b;
		if(b < mn) {
			mn = b;
			for(int i = 1; i <= n; i++) ans[i] = cur[i];
		}
		return;
	}
	last++;
	while(a * last <= b && last < mn) last++;
	for(; last < mn; last++) {
		if(a * last >= b * (n - x + 1)) break;
		cur[x] = last;
		dfs(x + 1, a * last - b, b * last, n, last);
	}
	return;
}

signed main() {
	n = read(), m = read();
	int i = 1; mn = 1e9;
	while(1) {
		dfs(1, n, m, i, 0);
		if(mn < 1e9) break;
		i++;
	}
	for(int j = 1; j <= i; j++) cout << ans[j] << " ";
	return 0;
}

LOJ #10023. 「一本通 1.3 練習 2」平板塗色

題目鏈接

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int A = 5e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar(); int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

int n;
vector <int> on[20];
struct node { int x1, y1, x2, y2, col; } a[A];
int ans = inf, vis[A];

bool check(int k) {
	int siz = (int)on[k].size();
	for(int i = 0; i < siz; i++) if(!vis[on[k][i]]) return 0;
	return 1;
}

void dfs(int tot, int fin, int col) {
	if(fin == n) {
		ans = min(ans, tot);
		return;
	}
	if(tot >= ans) return;
	for(int i = 1; i <= n; i++) {
		if(!vis[i] && check(i)) {
			vis[i] = 1;
			dfs(tot + (col != a[i].col), fin + 1, a[i].col);
			vis[i] = 0;
		}
	}
}

int main() {
	n = read();
	for(int i = 1; i <= n; i++) 
		a[i].x1 = read(), a[i].y1 = read(), a[i].x2 = read(), a[i].y2 = read(), a[i].col = read();
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			if(i != j && a[i].x1 == a[j].x2 && a[i].y2 >= a[j].y1 && a[i].y1 <= a[j].y2)
				on[i].push_back(j);
//	for(int i = 1; i <= n; i++) {
//		int t = on[i].size(); cout << "size: " <<  t << "\n";
//		for(int j = 0; j < t; j++) cout << on[i][j] <<" "; puts("");
//	}
	dfs(0, 0, 0);
	cout << ans << '\n'; return 0;
}

LOJ #2591. 「NOIP2009」靶形數獨

題目鏈接

特別想說一下這道題,因爲真的特!別!好!玩!

數獨都懂吧

1.1.每一行不能有一樣的數字
2.2.每一列不能有一樣的數字
3.3.每個333*3的九宮格不能有一樣的數字

首先上來的感覺就是要搜索,那麼,搜索的話,我們要想一下怎麼搜,這裏可以直接記錄某一行某個數是否出現,某一列某個數是否出現,某個九宮格內某個數是否出現,前兩個好弄,直接循環的時候記錄就好了,最後一個怎麼辦?

而且怎麼計算格子的分數呢??一個一個乘?

所以,我們要解決的問題是:怎麼計算格子的分數,怎麼判斷是第幾個九宮格

  1. 一開始在想怎麼計算這個格子的分數是多少,後來決定乾脆直接開個scorescore數組存下來得了,算的時候一乘就完事兒了
const int score[10][10] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 6, 6, 6, 6, 6, 6, 6, 6, 6},
{0, 6, 7, 7, 7, 7, 7, 7, 7, 6},
{0, 6, 7, 8, 8, 8, 8, 8, 7, 6},
{0, 6, 7, 8, 9, 9, 9, 8, 7, 6},
{0, 6, 7, 8, 9,10, 9, 8, 7, 6},
{0, 6, 7, 8, 9, 9, 9, 8, 7, 6},
{0, 6, 7, 8, 8, 8, 8, 8, 7, 6},
{0, 6, 7, 7, 7, 7, 7, 7, 7, 6},
{0, 6, 6, 6, 6, 6, 6, 6, 6, 6}, };
  1. 我的方法是,硬判斷,就像下面這樣(爲了美觀一些不需要判斷的條件我都打上了,反正影響也不大)
int pd(int i, int j) {
	if(i <= 3 && j <= 3) return 1;
	if(i <= 3 && j <= 6) return 2;
	if(i <= 3 && j <= 9) return 3;
	if(i <= 6 && j <= 3) return 4;
	if(i <= 6 && j <= 6) return 5;
	if(i <= 6 && j <= 9) return 6;
	if(i <= 9 && j <= 3) return 7;
	if(i <= 9 && j <= 6) return 8;
	if(i <= 9 && j <= 9) return 9;
}

然後我們就可以一行一行的搜索,填完一行再填下一行,然後就可以得到六十分的好成績

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int A = 5e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar();
	int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

const int score[10][10] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 6, 6, 6, 6, 6, 6, 6, 6, 6},
{0, 6, 7, 7, 7, 7, 7, 7, 7, 6},
{0, 6, 7, 8, 8, 8, 8, 8, 7, 6},
{0, 6, 7, 8, 9, 9, 9, 8, 7, 6},
{0, 6, 7, 8, 9,10, 9, 8, 7, 6},
{0, 6, 7, 8, 9, 9, 9, 8, 7, 6},
{0, 6, 7, 8, 8, 8, 8, 8, 7, 6},
{0, 6, 7, 7, 7, 7, 7, 7, 7, 6},
{0, 6, 6, 6, 6, 6, 6, 6, 6, 6}, };

int pd(int i, int j) {
	if(i <= 3 && j <= 3) return 1;
	if(i <= 3 && j <= 6) return 2;
	if(i <= 3 && j <= 9) return 3;
	if(i <= 6 && j <= 3) return 4;
	if(i <= 6 && j <= 6) return 5;
	if(i <= 6 && j <= 9) return 6;
	if(i <= 9 && j <= 3) return 7;
	if(i <= 9 && j <= 6) return 8;
	if(i <= 9 && j <= 9) return 9;
}

int a[11][11], hang[11][11], lie[11][11], sma[11][11];
int tot, ans = -10000;

void coun() {
	int now = 0;
	for(int i = 1; i <= 9; i++) 
		for(int j = 1; j <= 9; j++)
			now += a[i][j] * score[i][j];
	ans = max(ans, now);
	return;
}

void dfs(int ho, int li) { //行、列、這一行填了多少個 
	if(ho == 10) { coun(); return; }
	if(li == 10) dfs(ho + 1, 1);
	if(!a[ho][li]) {
		for(int i = 1; i <= 9; i++) {
			if(!hang[ho][i] && !lie[li][i] && !sma[pd(ho, li)][i]) {
				hang[ho][i] = lie[li][i] = sma[pd(ho, li)][i] = 1;
				a[ho][li] = i; dfs(ho, li + 1); a[ho][li] = 0;
				hang[ho][i] = lie[li][i] = sma[pd(ho, li)][i] = 0;
			}
		}
	}
	else dfs(ho, li + 1);
}

int main() {
	for(int i = 1; i <= 9; i++)
		for(int j = 1; j <= 9; j++) {
			a[i][j] = read();
			if(a[i][j]) {
				sma[pd(i, j)][a[i][j]] = 1;
				hang[i][a[i][j]] = 1;
				lie[j][a[i][j]] = 1;
			}
			else tot++;
		}
	dfs(1, 1);
	cout << ans << "\n";
	return 0;
}

怎麼優化?

我不會優化呀!!怎麼辦!只能搜了……只見大佬說

從填數最多的一行開始填,這樣要選擇的數就少了,不合法的情況就可以省掉一些

然後就滿分了qwqqwq

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int A = 5e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar();
	int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

const int score[10][10] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 6, 6, 6, 6, 6, 6, 6, 6, 6},
{0, 6, 7, 7, 7, 7, 7, 7, 7, 6},
{0, 6, 7, 8, 8, 8, 8, 8, 7, 6},
{0, 6, 7, 8, 9, 9, 9, 8, 7, 6},
{0, 6, 7, 8, 9,10, 9, 8, 7, 6},
{0, 6, 7, 8, 9, 9, 9, 8, 7, 6},
{0, 6, 7, 8, 8, 8, 8, 8, 7, 6},
{0, 6, 7, 7, 7, 7, 7, 7, 7, 6},
{0, 6, 6, 6, 6, 6, 6, 6, 6, 6}, };

int pd(int i, int j) {
	if(i <= 3 && j <= 3) return 1;
	if(i <= 3 && j <= 6) return 2;
	if(i <= 3 && j <= 9) return 3;
	if(i <= 6 && j <= 3) return 4;
	if(i <= 6 && j <= 6) return 5;
	if(i <= 6 && j <= 9) return 6;
	if(i <= 9 && j <= 3) return 7;
	if(i <= 9 && j <= 6) return 8;
	if(i <= 9 && j <= 9) return 9;
}

int a[11][11], hang[11][11], lie[11][11], sma[11][11];
int tot, ans = -1;
struct node { int sum, line; } qwq[11];

bool cmp(node a, node b) {
	return a.sum < b.sum;
}

void coun() {
	int now = 0;
	for(int i = 1; i <= 9; i++) 
		for(int j = 1; j <= 9; j++)
			now += a[i][j] * score[i][j];
	ans = max(ans, now);
	return;
}

void dfs(int cnt, int ho, int li) { //行、列、這一行填了多少個 
	if(cnt == 10) { coun(); return; }
	if(li == 10) dfs(cnt + 1, qwq[cnt + 1].line, 1);
	if(!a[ho][li]) {
		for(int i = 1; i <= 9; i++) {
			if(!hang[ho][i] && !lie[li][i] && !sma[pd(ho, li)][i]) {
				hang[ho][i] = lie[li][i] = sma[pd(ho, li)][i] = 1;
				a[ho][li] = i; dfs(cnt, ho, li + 1); a[ho][li] = 0;
				hang[ho][i] = lie[li][i] = sma[pd(ho, li)][i] = 0;
			}
		}
	}
	else dfs(cnt, ho, li + 1);
}

int main() {
	for(int i = 1; i <= 9; i++) {
		tot = 0;
		for(int j = 1; j <= 9; j++) {
			a[i][j] = read();
			if(a[i][j]) {
				sma[pd(i, j)][a[i][j]] = 1;
				hang[i][a[i][j]] = 1;
				lie[j][a[i][j]] = 1;
			}
			else tot++;
			qwq[i].line = i, qwq[i].sum = tot;
		}
	}
	sort(qwq + 1, qwq + 1 + 9, cmp);
	dfs(1, qwq[1].line, 1);
	cout << ans; return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章