DFS 常用剪枝及题目整理 UVA307 Sticks、POJ1011、HDU1426

DFS

剪枝

  • 排除等效冗余
    先搜1再搜2和先搜2再1相同,每次从上个搜索的地方搜起
  • 数论优化
    搜索过程中往往富含数学规律,常见的为搜索结果为某数的约数
  • 搜索顺序优化
    选择优秀的搜索顺序,比如从大到小搜索
  • 可行性剪枝
    搜索过程即可预判最终结果失败
  • 记忆化搜索
    将已搜索到的结果存起来,再次搜索可以直接得到答案
  • 最优化剪枝
    搜索过程即可以预判存在更优解或非最优解
  • 上下界剪枝
    搜索的起点和终点是有限制的
  • 奇偶性剪枝

在接下来的题目中,将详细讲解以上剪枝的应用

UVA307 小木棍 Sticks

POJ1011

题意

给你若干根短棒
将其组合成等长的木棒,所有短棒都要用完
尽可能短,并输出其长度

分析

  1. 搜索顺序优化:从长的木棍开始搜索
  2. 数论优化:组成木棍长度必定为所有木棍长度的约数
  3. 可行性剪枝:第一根都用不上,必定失败
  4. 排除等效冗余::记录上次搜索地方,每次从上个地方搜起
  5. 上下界剪枝:等长木棒的长度至少为最短木棒,最长不超过木棒长度总和

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#pragma warning (disable:4996)
const int maxn = 70;
bool cmp(int a, int b) {
	return a > b;
}
int n, sum, a[maxn];
bool vis[maxn];
int tot, maxl;
bool dfs(int s, int l, int ops) {
	if (s == tot - 1)return true;
	for (int i = ops; i <= n; i++) {
		if (vis[i]) continue;
		if (a[i] + l == maxl) {
			vis[i] = true;
			if (dfs(s + 1, 0, 1)) return true;
			vis[i] = false;
			return false;
		}
		else if (a[i] + l < maxl) {
			vis[i] = true;
			if (dfs(s, l + a[i], i + 1)) return true;
			vis[i] = false;
			if (l == 0) return false;
			while (a[i] == a[i + 1]) i++;
		}
	}
	return false;
}
int main() {
	while (~scanf("%d", &n)&&n) {
		sum = 0;
		for (int i = 1; i <= n; i++) {
			scanf("%d", &a[i]);
			sum += a[i];
		}
		sort(a + 1, a + 1 + n, cmp);
		for (int i = a[n]; i <= sum; i++) {
			if (sum % i) continue;
			tot = sum / i; maxl = i;
			fill(vis, vis + 1 + n, 0);
			if (dfs(0, 0, 1))
				break;
		}
		printf("%d\n", maxl);
	}
}

HDU1426 Sudoku Killer

题意

给出 9 ∗ 9 9*9 99的数独(填入数字使得每行每列每个数字都不同)
且保证有且只有一个解

分析

  1. 搜索顺序优化:显然先搜索?多的行,可能的方案明显要多,先从?少的行先搜起
  2. 搜索顺序优化:预处理空格,只需要搜索空格即可
  3. 可行性优化:一旦不可行,就返回失败
  4. 可行性优化:预处理每行可用的数
    一般用上前面一两个剪枝即可

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
typedef pair<int, int> pii;
int a[12][12], use[12][12];
pii num[12];
bool vis[12][12], can[12][12], f[12][12];
bool flag = false;
void dfs(int s, int x) {	//num[s], 第x列
	if (s == 10) { flag = true; return; }
	int i = num[s].second;
	for (int j = x; j <= 9; j++) {
		if (a[i][j]) {
			if (j == 9) {
				dfs(s + 1, 1);
				return;
			}
			else continue;
		}
		for (int k = 1; k <= use[i][0]; k++) {
			if (can[i][k]) continue;
			if (vis[j][use[i][k]])continue;
			if (f[(i - 1) / 3 * 3 + (j - 1) / 3][use[i][k]])continue;
			a[i][j] = use[i][k];
			can[i][k] = true;
			vis[j][use[i][k]] = true;
			f[(i - 1) / 3 * 3 + (j - 1) / 3][use[i][k]] = true;
			if (j == 9) dfs(s + 1, 1);
			else dfs(s, j + 1);
			if (flag) return;
			a[i][j] = 0;
			can[i][k] = false;
			vis[j][use[i][k]] = false;
			f[(i - 1) / 3 * 3 + (j - 1) / 3][use[i][k]] = false;
		}
		return;
	}
}
int main() {
	char s[100]; int g = 0;
	while (1) {
		memset(a, 0, sizeof(a));		//初始化
		memset(vis, 0, sizeof(vis));
		memset(f, 0, sizeof(f));
		memset(use, 0, sizeof(use));
		memset(can, 0, sizeof(can));
		memset(num, 0, sizeof(num));
		flag = false; char ch;
		for (int i = 1; i <= 9; i++) {
			for (int j = 1; j <= 9; j++) {
				if (!(cin >> ch))return 0;	//用cin输入
				if (ch >= '0' && ch <= '9')a[i][j] = ch - '0';
				if (ch == '?')
					num[i].first++, a[i][j] = 0;
				vis[i][a[i][j]] = true;
			}
			num[i].second = i;
		}
		for (int i = 1; i <= 9; i++) {		//记录每行可用的数
			for (int j = 1; j <= 9; j++) {
				if (!vis[i][j]) use[i][++use[i][0]] = j;
			}
		}
		memset(vis, 0, sizeof(vis));
		for (int i = 1; i <= 9; i++) {		//记录每列和每个九宫格已有的数
			for (int j = 1; j <= 9; j++) {
				vis[i][a[j][i]] = true;
				f[(i - 1) / 3 * 3 + (j - 1) / 3][a[i][j]] = true;
			}
		}
		sort(num + 1, num + 10);
		dfs(1, 1);
		if (g++)printf("\n");
		for (int i = 1; i <= 9; i++) {
			for (int j = 1; j <= 8; j++)
				printf("%d ", a[i][j]);
			printf("%d\n", a[i][9]);
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值