迭代加深搜索
DFS 最大的缺陷在于每个搜索分支深入过深
当答案在比较浅的位置的时候花费了过多的时间在无效搜索分支中
迭代加深搜索首先深度优先搜索 k 层
若没有找到可行解,再深度优先搜索 k+1 层,直到找到可行解为止
由于深度是从小到大逐渐增大的,所以当搜索到结果时可以保证搜索深度是最小的
本质:迭代加深本质上是通过枚举的手段为 DFS 增加了一个深度条件剪枝
Addition Chains
题意
给出一个数
n
n
n,你现在有一个
1
1
1
你可以将已有的数字相加得到一个新的数字,直到得到
n
n
n
输出你用的数列,要求长度最短
分析
迭代深搜
第一想法是bfs,搜索至少几个数字构成数列
但是构造数列需要dfs实现
也就是说这是一个bfs+dfs=迭代深搜
枚举数组长度,dfs
剪枝
- 至少p位数列 ( 2 p > = n ) (2^p>=n) (2p>=n)
- 显然构造一个递增数列,产生的新数必须更大
- 因为每次最多乘2倍,所以我们可以预判最大的数,是否能超过n
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
int n, a[233], k;
bool flag = false;
void dfs(int x) {
if (flag) return;
if (x == k + 1) {
if (a[x - 1] == n)
flag = true;
return;
}
for (int i = 1; i < x; i++) {
for (int j = i; j < x; j++) {
if (a[i] + a[j] > a[x - 1] && a[i] + a[j] <= n &&
(LL(a[i] + a[j]) << (k - x)) >= n) {
a[x] = a[i] + a[j];
dfs(x + 1);
if (flag)return;
}
}
}
}
int main() {
while (~scanf("%d", &n) && n) {
if (n == 1) {
printf("1\n");
continue;
}
memset(a, 0, sizeof(a));
int p = n; a[1] = 1; k = 0;
while (p) {
p >>= 1;
k++;
}
for (int i = 1; i <= n; i++) {
flag = false;
dfs(2);
if (flag) {
for (int j = 1; j < k; j++)
printf("%d ", a[j]);
printf("%d\n", a[k]);
break;
}
k++;
}
}
}
UVA1283 Infiltration
题意
有一些细胞可以控制另一些细胞,若你控制了一些细胞,那么就 控制了它直接控制的细胞,问需要控制多少细胞才可以控制所有 的细胞,每两个细胞之间都有控制和被控制的关系
- 给出 n(n≤ 75) 个细胞
- 有一些细胞可以控制另一些细胞,若你控制了一些细胞,那么就控制了它直接控制的细胞
- 每两个细胞之间都有控制和被 控制的关系
问需要控制多少细胞才可以控制所有的细胞
分析
一开始感觉点的数量很多,没什么想法
但是我们注意到所有细胞之间都有控制和被控制的关系
那么这就是一个完全图(竞赛图)
而竞赛图拥有一些优美的性质
- 如果有环,必然存在三元环
- 一定存在哈密顿路径
- 存在哈密顿回路的充要条件是强联通
- 最小点支配集大小不超过 log(n)
我们注意到最小点支配集不超过logn
支配集:令一个图为V-E,V为点集,E为边集,
支配集S为V的一个子集,使得S中任意一个点的连线和V-S中任意一个点的连线均属于边集E
所以,本题所需最多的点为logn
然后我们考虑搜索即可
最小点数需要bfs
输出由哪些点控制需要dfs
dfs+bfs=迭代加深搜索
枚举深度为1-logn
代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <bitset>
using namespace std;
#pragma warning (disable:4996)
typedef bitset<100> Bit;
const int maxn = 105;
int n, deep; bool flag;
int ans[maxn];
char s[maxn][maxn];
Bit b[maxn];
void init() {
for (int i = 1; i <= n; i++) {
b[i].reset(); b[i][i] = 1;
for (int j = 1; j <= n; j++)
if (s[i][j] == '1')
b[i][j] = 1;
}
deep = 1; flag = false;
memset(ans, 0, sizeof(ans));
}
void dfs(int x, int step, Bit k) {
if (step == deep + 1) {
if (k.count() == n) flag = true;
return;
}
for (int i = x; i <= n; i++) {
ans[step] = i;
dfs(i + 1, step + 1, k | b[i]);
if (flag)return;
}
}
int main() {
int g = 0;
while (~scanf("%d", &n)) {
for (int i = 1; i <= n; i++)
scanf("%s", s[i] + 1);
init();
for (int i = 1; i <= n; i++, deep++) {//logn时候肯定能截止
dfs(1, 1, Bit(0));
if (flag)break;
}
printf("Case %d: %d",++g, deep);
for (int i = 1; i <= deep; i++)
printf(" %d", ans[i]);
printf("\n");
}
}