Loading

【一本通提高】深搜的剪枝技巧做题记录

【一本通提高】深搜的剪枝技巧做题记录

题目编号 标题 估分 正确 提交
Y 2050 Problem  A 【一本通提高篇深搜的剪枝技巧】数的划分 --- 156 244
Y 2051 Problem  B 【一本通提高篇深搜的剪枝技巧】生日蛋糕 --- 111 220
Y 2052 Problem  C 【一本通提高篇深搜的剪枝技巧】小木棍 --- 154 585
Y 2053 Problem  D 【一本通提高篇深搜的剪枝技巧】Addition Chains --- 121 280
2054 Problem  E 【一本通提高篇深搜的剪枝技巧】weight --- 100 466
2055 Problem  F 【一本通提高篇深搜的剪枝技巧】埃及分数 --- 115 295
2056 Problem  G 【一本通提高篇深搜的剪枝技巧】平板涂色 --- 91 173
2057 Problem  H 【一本通提高篇深搜的剪枝技巧】质数方阵 --- 69 296
2058 Problem  I 【一本通提高篇深搜的剪枝技巧】 靶形数独 --- 85 130

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

  • 摘自bilibili
#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;
}
posted @ 2025-03-23 21:26  FrankWkd  阅读(23)  评论(0)    收藏  举报