Loading

「刷题记录」LOJ/一本通提高篇 深搜的剪枝技巧

「数的划分」

题目传送门:数的划分
思路:确定下限,每一次搜的数都不能小于前一次,同时,上限就是不能超出范围

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
int n, k, ans;

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

void dfs(int las, int sum, int cnt)
{
	if(cnt == k) {
		if(sum == n) {
			++ans;
		}
		return ;
	}
	for (int i = las; sum + i * (k - cnt) <= n; ++i) {
		dfs(i, sum + i, cnt + 1);
	}
}

int main() {
	n = read(), k = read();
	if(n == k) {
		printf("1\n");
		return 0;
	}
	dfs(1, 0, 0);
	printf("%d\n", ans);
	return 0;
}

「生日蛋糕」

题目传送门:生日蛋糕
思路:预处理出最小体积与面积,如果当前面积加上剩下还没涂的蛋糕的最小面积比最优解大,直接返回,如果当前体积加上剩下的蛋糕的最小体积已经超过 \(n\),直接返回
圆柱体侧面积公式: \(S = 2 \times \pi \times r \times h\)
圆柱体体积公式: \(V = \pi \times r^2 \times h\)
体积转化成面积公式为: \(V \times 2 \div r = S\)
如果剩余的体积转化成的面积加上原有的面积比最优解大,直接返回
枚举高时,我们可以确定下限,第 \(i\) 层的高的下限就是 \(i\)
上代码:

点击查看代码
/*
  date:2022.8.12
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 30;
const int inf = ~(1 << 31);
int n, m, ans = inf;
int r[N], h[N], mins[N], minv[N];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	
	return fg ? ~x + 1 : x;
}

void dfs(int rest, int r, int h, int s, int v) {// rest 还剩多少层 r 半径 h 高 s 面积 v 体积
	if (rest == 0) { // 到达最顶层,判断走人
		if (v == n) { // 体积等于n
			ans = min(ans, s);
		}
		
		return ;
	}
	
	if (s + mins[rest - 1] >= ans)
		return ;
	
	// 如果当前求出的面积已经不满足最优解了,直接return
	if (v + minv[rest - 1] > n)
		return ;
	
	// 如果当前求出的体积已经不满足最优解了,直接return
	if (s + 2 * (n - v) / r >= ans)
		return ;
	
	// 如果当前求出的面积+公式推出的剩余部分的面积已经大于最优解,直接return
	for (int rr = r - 1; rr >= rest; --rr) {
		if (rest == m) {
			s = rr * rr;//预处理出每层蛋糕上表面要涂的面积
		}
		
		int maxh = min(h - 1, (n - minv[rest - 1] - v) / rr / rr);// 处理当前这一层的高
		
		for (int hh = maxh; hh >= rest; --hh) {
			dfs(rest - 1, rr, hh, s + 2 * rr * hh, v + rr * rr * hh);//继续向下搜索
		}
	}
}

int main() {
	n = read();
	m = read();
	
	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, n, n, 0, 0);//m 剩余层数 n 半径 n 高 0 面积 0 体积
	
	if (ans == inf)
		printf("0\n");
	else
		printf("%d\n", ans);
	
	return 0;
}

「小木棍」

题目传送门:小木棍
很经典的题目,不会的看一下这篇博客 大佬的博客
代码:

点击查看代码
/*
  date:2022.8.28
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
#include <algorithm>
typedef long long ll;
using namespace std;

const int N = 70;
int n, sum, m, fg, le;
int len[N], nxt[N], vis[N];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

int cmp(int a, int b) {
	return a > b;
}

void dfs(int cur, int las, int re) {
	if(!re) {
		if(cur == m) {
			fg = 1;
			return ;
		}
		int i;
		for (i = 1; vis[i] && i <= n; ++i);
		vis[i] = 1;
		dfs(cur + 1, i, le - len[i]);
		vis[i] = 0;
		if(fg)	return ;
	}
	int l = las + 1, r = n, mid;
	while (l < r) {
		mid = (l + r) >> 1;
		if(len[mid] <= re)	r = mid;
		else	l = mid + 1;
	}
	for (int i = l; i <= n; ++i) {
		if(!vis[i]) {
			vis[i] = 1;
			dfs(cur, i, re - len[i]);
			vis[i] = 0;
			if(fg)	return ;
			if(re == len[i] || re == le)	return ;
			i = nxt[i];
			if(i == n)	return ;
		}
	}
}

int main() {
	n = read();
	for (int i = 1; i <= n; ++i) {
		len[i] = read();
		sum += len[i];
	}
	sort(len + 1, len + n + 1, cmp);
	nxt[n] = n;
	for (int i = n - 1; i; --i) {
		(len[i] == len[i + 1]) ? nxt[i] = nxt[i + 1] : nxt[i] = i;
	}
	int maxup = sum >> 1;
	fg = 0;
	for (le = len[1]; le <= maxup; ++le) {
		if(sum % le)	continue;
		m = sum / le;
		vis[1] = 1;
		dfs(1, 1, le - len[1]);
		vis[1] = 0;
		if(fg) {
			printf("%d\n", le);
			return 0;
		}
	}
	printf("%d\n", sum);
	return 0;
}

「Addition Chains」

题目传送门:Addition Chains
思路:这应该是迭代加深搜索,限制深度,如果当然搜到的深度比之前搜到的最优深度要大,直接返回
设当前层为 \(x\),还剩 \(d\) 层,当前层的数为 \(a\),下一层的数为 \(b\),那么 \(b \le 2 \times a\)
为什么?因为最差情况就是 \(2\)\(a\) 相加,又因为还剩 \(d\) 层,所以最差情况下最后一个数字为 \(a \times 2 ^ d\)
如果 \(a \times 2 ^ d < n\) 直接返回即可
代码:

点击查看代码
/*
  date: 2022.8.28
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
#include <algorithm>
typedef long long ll;
using namespace std;

const int N = 10005;
const int inf = ~(1 << 31);
int n, maxup;
int a[N], ans[N];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	
	return fg ? ~x + 1 : x;
}

void dfs(int dep) {
	if (dep - 1 > maxup)
		return ;
	
	if (a[dep - 1] > n)
		return ;
	
	if ((ll)a[dep - 1] * (1ll << (maxup - dep)) < n)	
		return ;
	
	if (a[dep - 1] == n) {
		if ((dep - 1) > maxup)
			return ;
		
		maxup = dep - 1;
		
		for (int i = 1; i <= maxup; ++i) {
			ans[i] = a[i];
		}
	}
	else {
		for (int i = dep - 1; i; --i) {
			if (a[dep - 1] + a[i] <= n) {
				a[dep] = a[dep - 1] + a[i];
				dfs(dep + 1);
				a[dep] = 0;
			}
		}
	}
}

int main() {
	while (1) {
		n = read();
		
		if (!n)	
			return 0;
		
		if (n == 1) {
			printf("1\n");
			continue;
		}
		
		maxup = inf;
		a[1] = 1;
		dfs(2);
		
		for (int i = 1; i <= maxup; ++i) {
			printf("%d", ans[i]);
			
			if (i != maxup)
				printf(" ");
			
		}
		
		printf("\n");
	}
	
	return 0;
}

「weight」

题目传送门:weight
思路:说白了,就是前缀和和后缀和顺序打乱了,先将数字按升序排序,最大的两个是左右数字的和,先排除掉,最小的两个,一定为队首和队尾,但具体谁在前谁在后,我们只能搜一下,剩下的数,可能是前缀和,也可能是后缀和,我们需要进行搜索,具体看代码吧
代码:

点击查看代码
/*
  date: 2022.8.28
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N = 1010;
const int M = 5e5 + 5;
int n, m, sum, fg;
int a[N << 1], s[M], ans[N];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	
	return fg ? ~x + 1 : x;
}

void dfs(int cur, int l, int r, int lval, int rval) {
	if (fg)
		return ;
	if (l == r) {
		if (!(s[a[cur] - lval] || s[a[cur] - rval]))
			return ;
		int tmp = a[n << 1] - lval - rval;
		if(tmp < 1 || tmp > 500)
			return ;
		ans[l] = tmp;
		fg = 1;
		for (int i = 1; i <= n; i ++)
			printf("%d ", ans[i]);
		return ;
	}
	if (s[a[cur] - lval]) {
		ans[l] = a[cur] - lval;
		dfs(cur + 1, l + 1, r, a[cur], rval);
	}
	if (s[a[cur] - rval]) {
		ans[r] = a[cur] - rval;
		dfs(cur + 1, l, r - 1, lval, a[cur]);
	}
}

int main() {
	n = read();
	int nn = n << 1;
	for (int i = 1; i <= nn; ++i) {
		a[i] = read();
	}
	m = read();
	for (int i = 1; i <= m ; ++i) {
		int x = read();
		s[x] = 1;
	}
	sort(a + 1, a + nn + 1);
	ans[1] = a[1];
	sum = a[nn];
	dfs(1, 1, n, 0, 0);
	return 0;
}

「埃及分数」

题目传送门:埃及分数
思路:迭代加深搜索
剪枝1:每次分母都要比前一次大,设 \(pre\) 为前一次搜到的分母,下一次就可以从pre + 1开始
优化2:
\(\frac{1}{a} < \frac{mol}{den}\)
\(den < mol \times a\)
\(a > \frac{den}{mol}\)
每一次搜索,分母也可以从当前还剩分数的倒数开始
优化3: 还剩 \((lim + 1 - dep)\) 个数
\(x = (lim + 1 - dep)\)
最差情况是每个数都等于 \(\frac{\frac{mol}{den}}{x}\)
\(\frac{\frac{mol}{den}}{x} = \frac{mol}{den \times x} = \frac{1}{den \times x \div mol}\)
所以每次搜索分母的上限就是 \(den \times x \div mol\)
从小到大枚举搜索深度,直接开搜
代码:

点击查看代码
/*
  date: 2022.8.29
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;

const int inf = ~(1 << 31);
const int N = 1010;
ll a, b, fg, ans, lim;
ll tot[N], num[N];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

void dfs(ll dep, ll mol, ll den, ll pre) {
	if (dep == lim + 1) {
		if (!mol) {
			fg = 1;
			if (num[lim] < tot[lim]) {
				for (ll i = 1; i <= lim; ++i) {
					tot[i] = num[i];
				}
				ans = num[lim];
			}
		}
		return ;
	}
	// 优化1:每次分母都要比前一次大,可以从pre + 1开始
	// 优化2: 1 / a < mol / den
	// den < mol * a
	// a > den / mol
	// 优化3: 还剩(lim + 1 - dep)个数,最差情况是每个数都等于 (mol / den) / x
	// (mol / den) / x = mol / (den * x) = 1 / (den * x / mol)
	int x = (lim + 1 - dep);
	for (ll i = max(pre + 1, den / mol); i <= den * x / mol; ++i) {
		num[dep] = i;
		dfs(dep + 1, mol * i - den, den * i, i);
	}
}

int main() {
	a = read();
	b = read();
	for (lim = 1; ; ++lim) {
		tot[lim] = inf;
		ans = inf;
		dfs(1, a, b, 1);
		if(fg)
			break;
	}
	for(int i = 1; i < lim; ++i) {
		printf("%lld ", tot[i]);
	}
	printf("%lld\n", tot[lim]);
	return 0;
}

「平板涂色」

题目传送门:平板涂色
说实话,不是很有技术含量
思路:每次枚举,判断哪些矩形已经符合被染色的条件,符合后,如果矩形的颜色与当前颜色相同,不用换刷子,否则,换刷子次数 \(+1\),如果次数已经比最优解大了,直接返回,最优性剪枝
代码:

点击查看代码
/*
  date: 2022.8.29
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long ll;

int n, ans = ~(1 << 31);
int vis[20], cnt[20];

struct matx {
	int xl, yl, xr, yr, col;
	vector<int> up;
} mat[20];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

void dfs(int tot, int fin, int col) {
	if (fin == n) {
		ans = min(ans, tot);
		return ;
	}
	if (tot >= ans)	return ;
	for (int i = 1; i <= n; ++i) {
		if(!vis[i] && !cnt[i]) {
			vis[i] = 1;
			int siz = mat[i].up.size();
			for (int j = 0; j < siz; ++j) {
				--cnt[mat[i].up[j]];
			}
			if(mat[i].col == col)
				dfs(tot, fin + 1, col);
			else
				dfs(tot + 1, fin + 1, mat[i].col);
			vis[i] = 0;
			for (int j = 0; j < siz; ++j) {
				++cnt[mat[i].up[j]];
			}
		}
	}
}

int main() {
	n = read();
	for (int i = 1; i <= n; ++i) {
		mat[i].xl = read();
		mat[i].yl = read();
		mat[i].xr = read();
		mat[i].yr = read();
		mat[i].col = read();
		
	}
	for (int i = 1; i <= n; ++i) {
		for(int j = 1; j <= n; ++j) {
			if (mat[j].xl != mat[i].xr)	continue;
			if (i == j || mat[i].yl >= mat[j].yr || mat[i].yr <= mat[j].yl)	continue;
			mat[i].up.push_back(j);
			++cnt[j];
		}
	}
	dfs(0, 0, 0);
	printf("%d\n", ans);
	return 0;
}

「质数方阵」

题目传送门:质数方阵
感觉像模拟。。。
直接上代码吧,这题让我有点崩溃

点击查看代码
/*
  date: 2022.8.29
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
#define A a[cnt]
using namespace std;
typedef long long ll;

const int N = 1e6 + 5;
int sum, n, m, cnt = 1;
int a[10010][8][8];
string ans[10010];
int vis[N + 10];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

int calc(int a1, int a2, int a3, int a4, int a5) {
	return (a1 * 10000 + a2 * 1000 + a3 * 100 + a4 * 10 + a5);
}

int check(int x) {
	int t = sqrt(x);
	
	for (int i = 2; i <= t; ++i) {
		if (x % i == 0)
			return 0;
	}
	
	return 1;
}


void make_prime() {
	for (int i1 = 1; i1 <= 9; ++i1) {
		for (int i2 = 0; i2 <= 9; ++i2) {
			for (int i3 = 0; i3 <= 9; ++i3) {
				for (int i4 = 0; i4 <= 9; ++i4) {
					for (int i5 = 1; i5 <= 9; ++i5) {
						int t = calc(i1, i2, i3, i4, i5);
						if ((i1 + i2 + i3 + i4 + i5 == sum) && check(t))
							vis[t] = 1;
					}
				}
			}
		}
	}
}

void qsort(int l, int r) {
	int i = l, j = r;
	string mid = ans[(l + r) >> 1];
	do {
		while (ans[i] < mid)
			++i;
		while (ans[j] > mid)
			--j;
		if (i <= j) {
			swap(ans[i], ans[j]);
			++i;
			--j;
		}
	} while (i <= j);
	if (l < j)
		qsort(l, j);
	if (i < r)
		qsort(i, r);
}

void print() { //输出答案
	--cnt;
	if (cnt == 0) {
		printf("NONE");
		return ;
	}
	for (int i = 1; i <= cnt; ++i) {
		for (int j = 1; j <= 5; ++j) {
			for (int k = 1; k <= 5; ++k)
				ans[i] = ans[i] + (char)(a[i][j][k] + 48);
		}
	}
	qsort(1, cnt);
	for (int i = 1; i <= cnt; ++i) {
		for (int j = 0; j <= 20; j += 5) {
			for (int k = j; k < j + 5; ++k)
				putchar(ans[i][k]);
			printf("\n");
		}
		printf("\n");
	}
}

void dfs13() {
	for (int i = 1; i <= 9; i += 2) {
		A[5][3] = i;
		A[5][4] = sum - A[5][1] - A[5][2] - A[5][3] - A[5][5];
		if (A[5][4] < 0 || A[5][4] > 9)
			continue;
		int t1 = calc(A[5][1], A[5][2], A[5][3], A[5][4], A[5][5]);
		if (!vis[t1])
			continue;
		A[1][3] = sum - A[2][3] - A[3][3] - A[4][3] - A[5][3];
		if (A[1][3] < 0 || A[1][3] > 9)
			continue;
		int t2 = calc(A[1][3], A[2][3], A[3][3], A[4][3], A[5][3]);
		if (!vis[t2])
			continue;
		A[1][4] = sum - A[1][1] - A[1][2] - A[1][3] - A[1][5];
		if (A[1][4] < 0 || A[1][4] > 9)
			continue;
		int t3 = calc(A[1][1], A[1][2], A[1][3], A[1][4], A[1][5]);
		if (!vis[t3])
			continue;
		if (A[1][4] + A[2][4] + A[3][4] + A[4][4] + A[5][4] != sum)
			continue;
		int t4 = calc(A[1][4], A[2][4], A[3][4], A[4][4], A[5][4]);
		if (!vis[t4])
			continue;
		++cnt;
		for (int j = 1; j <= 5; ++j) {
			for (int k = 1; k <= 5; ++k)
				A[j][k] = a[cnt - 1][j][k];
		}
	}
}

void dfs12() {
	for (int i = 1; i <= 9; i += 2) {
		A[5][2] = i;
		A[1][2] = sum - A[2][2] - A[3][2] - A[4][2] - A[5][2];
		if (A[1][2] < 0 || A[1][2] > 9)
			continue;
		int t = calc(A[1][2], A[2][2], A[3][2], A[4][2], A[5][2]);
		if (vis[t])
			dfs13();
	}
}

void dfs11() {
	for (int i = 1; i <= 9; i += 2) {
		A[3][5] = i;
		A[4][5] = sum - A[1][5] - A[2][5] - A[3][5] - A[5][5];
		if (A[4][5] < 0 || A[4][5] > 9)
			continue;
		int t1 = calc(A[1][5], A[2][5], A[3][5], A[4][5], A[5][5]);
		if (!vis[t1])
			continue;
		A[3][2] = sum - A[3][1] - A[3][3] - A[3][4] - A[3][5];
		if (A[3][2] < 0 || A[3][2] > 9)
			continue;
		int t2 = calc(A[3][1], A[3][2], A[3][3], A[3][4], A[3][5]);
		if (!vis[t2])
			continue;
		A[4][3] = sum - A[4][1] - A[4][2] - A[4][4] - A[4][5];
		if (A[4][3] < 0 || A[4][3] > 9)
			continue;
		int t3 = calc(A[4][1], A[4][2], A[4][3], A[4][4], A[4][5]);
		if (!vis[t3])
			continue;
		dfs12();
	}
}

void dfs10() {
	for (int i = 0; i <= 9; ++i) {
		A[3][4] = i;
		dfs11();
	}
}

void dfs9() {
	for (int i = 1; i <= 9; i += 2) {
		A[1][5] = i;
		A[4][2] = sum - A[1][5] - A[2][4] - A[3][3] - A[5][1];
		if (A[4][2] < 0 || A[4][2] > 9)
			continue;
		int t = calc(A[5][1], A[4][2], A[3][3], A[2][4], A[1][5]);
		if (vis[t])
			dfs10();
	}
}

void dfs8() {
	for (int i = 1; i <= 9; i += 2) {
		A[2][5] = i;
		A[2][3] = sum - A[2][1] - A[2][2] - A[2][4] - A[2][5];
		if (A[2][3] < 0 || A[2][3] > 9)
			continue;
		int t = calc(A[2][1], A[2][2], A[2][3], A[2][4], A[2][5]);
		if (vis[t])	dfs9();
	}
}

void dfs7() {
	for (int i = 0; i <= 9; ++i) {
		A[2][4] = i;
		dfs8();
	}
}

void dfs6() {
	for (int i = 1; i <= 9; ++i) {
		A[2][1] = i;
		A[3][1] = sum - A[1][1] - A[2][1] - A[4][1] - A[5][1];
		if (A[3][1] < 0 || A[3][1] > 9)
			continue;
		int t = calc(A[1][1], A[2][1], A[3][1], A[4][1], A[5][1]);
		if (vis[t])	dfs7();
	}
}

void dfs5() {
	for (int i = 1; i <= 9; ++i) {
		A[4][1] = i;
		dfs6();
	}
}

void dfs4() {
	for (int i = 1; i <= 9; i += 2) {
		A[5][1] = i;
		dfs5();
	}
}

void dfs3() {
	for (int i = 0; i <= 9; ++i) {
		A[2][2] = i;
		A[3][3] = sum - A[1][1] - A[2][2] - A[4][4] - A[5][5];
		if (A[3][3] < 0 || A[3][3] > 9)	
			continue;
		int t = calc(A[1][1], A[2][2], A[3][3], A[4][4], A[5][5]);
		if (vis[t])
			dfs4();
	}
}

void dfs2() {
	for (int i = 0; i <= 9; ++i) {
		A[4][4] = i;
		dfs3();
	}
}

void dfs1() {
	for (int i = 1; i <= 9; i += 2) {
		A[5][5] = i;
		dfs2();
	}
}

int main() {
	sum = read();
	a[1][1][1] = read();
	make_prime();
	dfs1();
	print();
	return 0;
}

「靶形数独」

题目传送门:靶形数独
思路:感觉像一个变了形的八皇后,只不过还要处理所在的宫和当前格子的分数,搜索时按行来搜,先从 \(0\) 的个数少的行来搜
代码:

点击查看代码
/*
  date: 2022.8.30
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;

int tot, cnt, ans = -1;
int a[10][10], no[100][4];
int hang[10][10] = {0}, lie[10][10] = {0}, ge[10][10] = {0};

struct node {
	int id, sum;
} h[10];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

int where(int x, int y) {
	if (x <= 3) {
		if (y <= 3)
			return 1;
		if (y > 3 && y <= 6)
			return 2;
		if (y > 6 && y <= 9)
			return 3;
	}
	if (x > 3 && x <= 6) {
		if (y <= 3)
			return 4;
		if (y > 3 && y <= 6)
			return 5;
		if (y > 6 && y <= 9)
			return 6;
	}
	if (x > 6 && x <= 9) {
		if (y <= 3)
			return 7;
		if (y > 3 && y <= 6)
			return 8;
		if (y > 6 && y <= 9)
			return 9;
	}
	return 0;
}

int point(int x, int y) {
	if (x == 1 || x == 9 || y == 1 || y == 9)
		return 6;
	if (x == 2 || x == 8 || y == 2 || y == 8)
		return 7;
	if (x == 3 || x == 7 || y == 3 || y == 7)
		return 8;
	if (x == 4 || x == 6 || y == 4 || y == 6)
		return 9;
	return 10;
}

int cmp (node a, node b) {
	return a.sum < b.sum;
}

void dfs(int fin, int s) {
	if (fin == cnt) {
		ans = max(ans, s);
//		cout << "*" << endl;
		return ;
	}
	for (int i = 1; i <= 9; ++i) {
		int x = no[fin + 1][0], y = no[fin + 1][1];
		int g = no[fin + 1][2], pi = no[fin + 1][3];
		if (!hang[x][i] && !lie[y][i] && !ge[g][i]) {
			hang[x][i] = lie[y][i] = ge[g][i] = 1;
			dfs(fin + 1, s + pi * i);
			hang[x][i] = lie[y][i] = ge[g][i] = 0;
		}
	}
}

int main() {
	for (int i = 1; i <= 9; ++i) {
		h[i].id = i;
	}
	for (int i = 1; i <= 9; ++i) {
		for (int j = 1; j <= 9; ++j) {
			a[i][j] = read();
			if (a[i][j] > 0) {
				hang[i][a[i][j]] = lie[j][a[i][j]] = ge[where(i, j)][a[i][j]] = 1;
				tot += a[i][j] * point(i, j);
			}
			else {
				++h[i].sum;
			}
		}
	}
	sort(h + 1, h + 10, cmp);
	for (int i = 1; i <= 9; ++i) {
		for (int j = 1; j <= 9; ++j) {
			if (a[h[i].id][j] == 0) {
				no[++cnt][0] = h[i].id;
				no[cnt][1] = j;
				no[cnt][2] = where(h[i].id, j);
				no[cnt][3] = point(h[i].id, j);
			}
		}
	}
	dfs(0, tot);
	printf("%d\n", ans);
	return 0;
}

结束

这些题难度都很大,有些是真的让人心累

posted @ 2022-08-30 11:02  yi_fan0305  阅读(49)  评论(0编辑  收藏  举报