【一本通提高】深搜的剪枝技巧做题记录
Problem A
- 直接暴力 \(dfs\) 分段,需要加几个剪枝,如下:
- 当份数大于 \(m\) 的时候,直接 \(return\) ;
- 当分出来的所有的份的和大于 \(n\) 的时候,\(return\).
#include <bits/stdc++.h>
using namespace std;
int n, k, ans;
void dfs(int pre, int tot, int part) {
if (tot == 0 and part == 0) {
ans++;
return ;
}
// cout << pre << " " << tot << " " << part << endl;
if(part <= 0) return ;
if (part > k + 1) return;
for (int i = pre; i <= tot; i++) {
dfs(i, tot - i, part - 1);
}
}
int main() {
cin >> n >> k;
dfs(1, n, k);
cout << ans << endl;
}
Problem B
#include <bits/stdc++.h>
using namespace std;
const int Inf = 1e9;
int minv[30],mins[20],n,m;
int ans = Inf;
void dfs(int u,int r,int h,int v,int s){
if(u == 0){
if(s < ans and v == n) ans = s;
return ;
}
if(v+minv[u] > n) return;
if(s+mins[u]>=ans) return;
if(s+2*(n-v)/r >= ans) return;
for(int i = min(r-1,(int)sqrt(n-v));i >= u;i--){
for(int j = min(h-1,(n-v)/(i*i));j >= u;j--){
dfs(u-1,i,j,v+i*i*j,s+2*i*j+(u==m?i*i:0));
}
}
}
int main(){
cin>>n>>m;
for(int i = 1;i <= m;i++){
mins[i] = mins[i-1]+2*i*i;
minv[i] = minv[i-1]+i*i*i;
}
dfs(m,Inf,Inf,0,0);//从下向上遍历
if(ans == Inf) puts("0");
else cout<<ans<<endl;
}
Problem C
#include <bits/stdc++.h>
using namespace std;
const int N = 1170;
int a[N];
int n, sum, len;
bool used[N];
bool cmp(int x, int y) {
return x > y;
}
bool dfs(int u, int cur, int start) {
if (u * len == sum) return true;
if (cur == len) return dfs(u + 1, 0, 0);
for (int i = start; i < n; i++) {
if (used[i]) continue;
if (cur + a[i] <= len) {
used[i] = true;
if (dfs(u, cur + a[i], i + 1)) return true;
used[i] = false;
// 如果当前木棍无法使用,且是该组的第一个木棍,则直接返回 false
if (cur == 0) return false;
if (cur + a[i] == len) return false;
// 如果当前木棍无法使用,且下一个木棍长度相同,则跳过
while (i + 1 < n && a[i + 1] == a[i]) i++;
}
}
return false;
}
int main() {
scanf("%d", &n);
sum = 0;
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
sum += a[i];
}
// 降序排序
sort(a, a + n, cmp);
// 枚举目标长度
for (len = a[0]; len <= sum; len++) {
if (sum % len == 0) {
memset(used, false, sizeof(used));
if (dfs(0, 0, 0)) {
printf("%d\n", len);
break;
}
}
}
return 0;
}
Problem D



#include <bits/stdc++.h>
using namespace std;
int a[1010], n;
int d;
bool dfs(int u) { // 搜索第 u 个数
if (u == d) return a[u] == n;
for (int i = u; i >= 1; i--) {
for (int j = i; j >= 1; j--) {
int t = a[i] + a[j];
if (t > n) continue;
if (t < a[u]) continue; // 保证数列递增
int backup = a[u + 1];
a[u + 1] = t;
// 估价:如果后续即使每次都选择最大可能的数相加也无法达到 n,则剪枝
for (int k = u + 2; k <= d; k++) {
t *= 2;
}
if (t < n) {
a[u + 1] = backup;
return false;
}
if (dfs(u + 1)) return true;
a[u + 1] = backup; // 回溯
}
}
return false;
}
int main() {
a[1] = 1;
while (cin >> n) {
if (n == 0) break;
d = 1;
while (!dfs(1)) {
d++; // 迭代加深
}
for (int i = 1; i <= d; i++) {
cout << a[i];
if (i != d + 1 - 1) cout << " ";
}
printf("\n");
}
return 0;
}