Part1.3 基础算法-深搜的剪枝技巧
- A [数的划分](https://ac.nowcoder.com/acm/contest/952/A)
- B [生日蛋糕](https://ac.nowcoder.com/acm/contest/952/B)
- C [小木棍](https://ac.nowcoder.com/acm/contest/952/C)
- D [Addition Chains](https://ac.nowcoder.com/acm/contest/952/D)
- E [weight](https://ac.nowcoder.com/acm/contest/952/E)
- F [埃及分数](https://ac.nowcoder.com/acm/contest/952/F)
- G [平板涂色](https://ac.nowcoder.com/acm/contest/952/G)
A 数的划分
这个题还是要好好看一下的,用自己的想法和思路。
#include <cstdio>
using namespace std;
const int N = 10010;
int n, k;
int a[N];
int ans;
void dfs(int u, int val)
{
if (val <= 0) return;
if (u >= k)
{
ans ++;
return;
}
for (int i = a[u - 1]; i <= val / (k - u + 1); i ++ )
{
a[u] = i;
val -= i;
dfs(u + 1, val);
val += i;
}
}
int main()
{
scanf("%d%d", &n, &k);
a[0] = 1;
dfs(1, n);
printf("%d\n", ans);
return 0;
}
B 生日蛋糕
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 10010;
int n, m;
int r[N], h[N];
int minv[N], mins[N];
int ans = 1 << 30;
void dfs(int u, int v, int s)
{
// printf("%d ^ %d ^ %d\n", u, v, s);
//剪枝
//当前体积为m~u+1的体积,加上最小的预估1~u的体积
if (v + minv[u] > n) return;
//如果最小也等于ans的话,也不用尝试了,已经有比他更优的方案了。
if (s + mins[u] >= ans) return;
if (s + 2 * (n - v) / r[u + 1] >= ans) return;
if (u < 0) return;
if (u == 0)
{
if (v == n) ans = min(ans, s);
return;
}
//往下搜索,循环r和h
//从大到小的循环顺序
for (int i = min(r[u + 1] - 1, (int)(sqrt(n - v))); i >= u; i -- )
for (int j = min(h[u + 1] - 1, (n - v) / i / i); j >= u; j -- )
{
// if (u == m) s += i * i;
// r[u] = i, h[u] = j;
// dfs(u - 1, v + i * i * j, s + 2 * i * j);
int t = 0;
if (u == m) t = i * i;
r[u] = i, h[u] = j;
dfs(u - 1, v + i * i * j, s + 2 * i * j + t);
}
}
int main()
{
scanf("%d%d", &n, &m);
//预估函数
for (int i = 1; i <= m; i ++ ) minv[i] = minv[i - 1] + i * i * i;
for (int i = 1; i <= m; i ++ ) mins[i] = mins[i - 1] + i * i * 2;
//给当u为m使用的
r[m + 1] = h[m + 1] = 1 << 30;
//需要传参,当前层数,当前体积,当前面积
dfs(m, 0, 0);
if (ans == 1 << 30) ans = 0;
printf("%d\n", ans);
return 0;
}
C 小木棍
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 110;
int n;
int a[N];
int sum;
bool st[N];
//已经拼好has-1个,正在拼当前的第has个小木棍,长度为len,个数为cnt个,现在正在拼的长度为lens,当前行上一个拼的木棍的下标
bool dfs(int has, int len, int cnt, int lens, int last)
{
if (has > cnt) return true;//已经拼好了
//当前的小木棍已经拼好了,可以开始拼下一个小木棍了
if (lens == len)
{
if (dfs(has + 1, len, cnt, 0, 1)) return true;
}
//现在搞得是当前层,和下一层没有关系
//枚举当前层,进行木棍的拼凑
int fail = 0;
for (int i = last; i <= n; i ++ )
if (!st[i])
{
if (lens + a[i] > len) continue;
if (fail == a[i]) continue;
// if (lens + a[i] > len) return false;
// if (fail == a[i]) return false;
st[i] = true;
if (dfs(has, len, cnt, lens + a[i], i + 1)) return true;
fail = a[i];
st[i] = false;
//如果现在已经拼好了,或者要进入下一层了,都不需要继续搜索了
// printf("%d %d %d\n", lens, a[i], lens + a[i]);
if (lens == 0) return false;
}
return false;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &a[i]);
sum += a[i];
}
sort(a + 1, a + 1 + n);
reverse(a + 1, a + 1 + n);
// printf("%d %d %d\n", a[n], sum, n);
for (int len = a[n]; len <= sum; len ++ )
if (sum % len == 0)
{
// printf("%d %d\n", len, sum / len);
for (int i = 0; i <= n; i ++ ) st[i] = false;
if (dfs(1, len, sum / len, 0, 1))
{
printf("%d\n", len);
return 0;
}
}
}
D Addition Chains
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
int n;
int a[N];
int depth;
bool st[N];
bool dfs(int u)
{
// cout << u << "\n";
if (u > depth) return false;
if (u == depth && a[u - 1] == n) return true;
memset(st, 0, sizeof st);
for (int i = u - 1; i >= 0; i -- )
for (int j = i; j >= 0; j -- )
{
int t = a[i] + a[j];
if (t > n) continue;//剪枝
if (t <= a[u - 1]) continue;//剪枝
if (t > 2 * a[u - 1]) continue;//如果出现这情况,说明我们之前可以将这些枚举到了,当然,可能和st数组重复了
if (st[t]) continue;
st[t] = true;
a[u] = t;
if (dfs(u + 1)) return true;
}
return false;
}
int main()
{
while (cin >> n)
{
if (!n) return 0;
a[0] = 1;
for (depth = 0; !dfs(1); depth ++ );
for (int i = 0; i < depth; i ++ )
printf("%d ", a[i]);
puts("");
}
}
E weight
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2010, M = 510;
int n, m;
int a[N], b[M];
int path[N];
void dfs(int u, int L, int R, int s, int t)
{
if (u > 2 * n + 1) return;
if (L == R)
{
if (a[2 * n] - s - t <= 500 && b[a[2 * n] - s - t])
{
path[L] = a[2 * n] - s - t;
for (int i = 1; i <= n; i ++ )
printf("%d ", path[i]);
puts("");
exit(0);
}
return;
}
if (a[u] - s <= 500 && b[a[u] - s])
{
path[L] = a[u] - s;
dfs(u + 1, L + 1, R, a[u], t);
}
if (a[u] - t <= 500 && b[a[u] - t])
{
path[R] = a[u] - t;
dfs(u + 1, L, R - 1, s, a[u]);
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n << 1; i ++ ) cin >> a[i];
cin >> m;
for (int i = 1; i <= m; i ++ ) {int x; cin >> x; b[x] ++;}
sort(a + 1, a + 1 + 2 * n);
dfs(1, 1, n, 0, 0);
return 0;
}
F 埃及分数
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1010, M = 1e7;
ll n, m, depth;
int path[N], ans[N];
ll gcd(ll a, ll b)
{
return b ? gcd(b, a % b) : a;
}
bool dfs(int u, ll a, ll b)
{
if (u == depth - 1)
{
ll d = gcd(a, b);
a /= d;
b /= d;
// if (b % a) return false;
if (a == 1)
{
path[u] = b;
if (path[u - 1] >= path[u]) return false;
if (!ans[u] || ans[u] > path[u])
for (int i = 0; i < depth; i ++ )
ans[i] = path[i];
return true;
}
return false;
}
//1/i < a/b 也就是 i > b / a,即i的下界为b/a+1
//因为a/b - 1/i(depth - u) <= 0,这是i的上界
bool flag = false;
for (int i = max((int)(b / a) + 1, path[u - 1] + 1); a * i - b * (depth - u) <= 0; i ++ )
{
ll x = a * i - b;
ll y = b * i;
ll d = gcd(x, y);
x /= d;
y /= d;
if (x < 0 || y < 0) continue;
path[u] = i;
if (dfs(u + 1, x, y)) flag = 1;
}
return flag;
}
int main()
{
cin >> n >> m;
ll d = gcd(n, m);
n /= d;
m /= d;
if (n == 1)
{
cout << m << endl;
}
else
{
for (depth = 2; !dfs(0, n, m); depth ++ );
for (int i = 0; i < depth; i ++ )
printf("%d ", ans[i]);
return 0;
}
}
G 平板涂色
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 14;
struct Node
{
int y1, x1, y2, x2, c;
}a[N];
int n;
vector<int> v[N];
int ans = 1e9;
bool st[N];
//判断当前矩形是否可以染色,如果上面没有矩形或者上面的矩形都已经染好色了,就返回true
bool check(int u)
{
if (v[u].empty()) return true;
for (int i = 0; i < v[u].size(); i ++ )
if (!st[v[u][i]]) return false;
return true;
}
//cnt:已经染了多少个,res:使用了多少中颜色,c:上一个颜色是哪一种颜色
void dfs(int cnt, int res, int c)
{
if (res >= ans) return;
if (cnt == n)
{
ans = res;
return;
}
for (int i = 0; i < n; i ++ )
{
if (st[i]) continue;
if (check(i))
{
st[i] = true;
dfs(cnt + 1, res + (c != a[i].c), a[i].c);
st[i] = false;
}
}
}
int main()
{
cin >> n;
for (int i = 0; i < n; i ++ )
{
int x1, y1, x2, y2, c;
cin >> y1 >> x1 >> y2 >> x2 >> c;
a[i] = {y1, x1, y2, x2, c};
}
//将当前矩形的正上方的矩形记录下来
for (int i = 0; i < n; i ++ )
for (int j = 0; j < n; j ++ )
if (i != j && a[i].y1 == a[j].y2 && a[j].x1 <= a[i].x2 && a[j].x2 >= a[i].x1)
{
v[i].push_back(j);
}
dfs(0, 0, -1);
cout << ans << endl;
// for (int i = 0; i < n; i ++ )
// {
// printf("%d : ", i);
// for (auto it : v[i])
// cout << it << " ";
// cout << endl;
// }
return 0;
}