int n, m; int w[N]; int sum[N]; // 每辆车当前载重量,最多n辆车 int ans = N; // 注意初始赋值,求最小值,所以初始为最糟情况或无穷大
// u表示猫的下标,k表示将新开的车的下标,也是已开的车的数量 voiddfs(int u, int k) { // 最优性剪枝 // 如果车的数量已经大于等于当前最优解就剪枝 if (k >= ans) return; // 所有猫都放好了就返回 if (u == n){ ans = k; return; }
for (int i = 0; i < k; i ++ ) if (sum[i] + w[u] <= m){ // 可行性剪枝 // 如果当前车中的重量加上新猫的重量大于最大载重,就剪枝(不执行搜索也可以认为是剪枝) sum[i] += w[u]; dfs(u + 1, k); sum[i] -= w[u]; // 恢复现场 }
int minv = 10; // 存储分支最小的空格的分支数,初值大于最遭情况 int x, y; // 求分支最小的空格 for (int i = 0; i < N; ++ i) for (int j = 0; j < N; ++ j) if (str[i * N + j] == '.'){ // 获取填写情况对应的二进制数 int state = get(i, j); if (ones[state] < minv){ minv = ones[state]; x = i, y = j; } }
int state = get(x, y); // 枚举取出所有1,尝试所有能填的数 for (int i = state; i; i -= lowbit(i)){ int t = map[lowbit(i)]; // 转换为对应的填写数字(取对数),注意查找使用的是lowbit(i) draw(x, y, t, true); // 更新填写 if (dfs(cnt - 1)) returntrue; // 本题只有唯一解, 为真直接返回,否则回溯时会删除获得的结果 draw(x, y, t, false); // 恢复原状 } returnfalse; }
intmain(){ // 打表 for (int i = 0; i < N; ++ i) map[1 << i] = i; // 1 << i为2^i的值 // map用于存储2^k取对数为多少(即k),因为lowbit求出的是2^k,而我们想要的是k
// 打表法,预处理出每个数的二进制中有多少个1 // 由于一共9个数,所以用0~8位表示即可,故枚举到(1 << 9) - 1,即M for (int i = 0; i < M; ++ i) for (int j = 0; j < N; ++ j) // 位运算 // 求n的二进制表示中第k位是0还是1,n >> k && 1 ones[i] += i >> j & 1; while (cin >> str, str[0] != 'e'){ // 预处理表示行、列、九宫格状态的数组,初始化为全1 init(); // cnt表示还有多少个空位 int cnt = 0; // 填入输入的状态 for (int i = 0, k = 0; i < N; ++ i) for (int j = 0; j < N; ++ j, ++ k) if (str[k] != '.'){ // 数字的大小 // 这里写成-'1'而不是-'0'是为了便于后面求二进制时使用 // 因为二进制时t表示位数,而填写的数字为t + '1',所以这里统一格式 int t = str[k] - '1'; // 填入对应状态 draw(i, j, t, true); } else ++ cnt;
# DFS之剪枝——AcWing 166. 数独 # 剪枝优化+位运算+打表 # python会超时 defmain(): N = 9 M = 1 << N ones = [0] * M maps = {} for i inrange(M): for j inrange(N): ones[i] += i >> j & 1 for i inrange(N): maps[1 << i] = i defdraw(x, y, t, is_set): if is_set: s[x * N + y] = str(t) else: s[x * N + y] = '.' v = 1 << (t - 1) ifnot is_set: v = -v row[x] -= v col[y] -= v cell[x // 3][y // 3] -= v # row[x] ^= v # col[y] ^= v # cell[x//3][y//3] ^= v deflowbit(x): return x & -x defget(x, y): return row[x] & col[y] & cell[x // 3][y // 3] defbacktrace(cnt): ifnot cnt: returnTrue minv = 10 x, y = 0, 0 for i inrange(N): for j inrange(N): if s[i * N + j] == '.': state = get(i, j) if ones[state] < minv: minv = ones[state] x, y = i, j state = get(x, y) while state: t = maps[lowbit(state)] + 1 # 这里的加1一定不要少 # 因为maps中返回的t表示的位数,而在二进制中第0位表示的是1,第i位表示的是i+1, # 所以要填写的数字时t + 1 draw(x, y, t, True) if backtrace(cnt - 1): returnTrue draw(x, y, t, False) state -= lowbit(state) returnFalse whileTrue: g = input() if g == "end": return s = list(g) # python中字符串是一个不可变类型,不能直接改变字符串中某个字符的值, # 需要另建一个存字符的列表处理字符
col = [(1 << N) - 1] * N; row = [(1 << N) - 1] * N cell = [[(1 << N ) - 1] * 3for _ inrange(3)]
# cnt统计棋盘中空位个数 cnt = 0 for i inrange(N): for j inrange(N): if s[i * N + j] != '.': t = int(s[i * N + j]) draw(i, j, t, True) else: cnt += 1 backtrace(cnt) print("".join(s)) # 将列表中字符重新合并成字符串
int n, m; // R,H存每一层半径和高度 int R[N], H[N]; // 前u层总体积,总侧面积的最小值,相当于前缀和 int minv[N], mins[N]; int ans = INF; // 表面积答案
// u表示当前这一层编号,v表示从m到u+1层的总体积(不含本层),s表示从m到u+1层的总共的面积(不含本层) voiddfs(int u, int v, int s){ // 可行性剪枝——体积约束,已构建层的总体积+待构件层的最小总体积<=总体积 if (v + minv[u] > n) return; // 最优性剪枝——最优表面积,已构建层的表面积+待构件层的最小表面积<当前最优表面积 if (s + mins[u] >= ans) return; // 最优性剪枝——放缩不等式,已构建层的表面积+待构件层的最小表面积<当前最优表面积 if (s + 2*(n-v)/R[u+1] >= ans) return; // u = 0时表明已经枚举完毕,判断当前体积是否等于n,若是,更新表面积 if (!u){ if (n == v) ans = s; // 注意当前总体积不等于n是不要更新面积,但也要return return; } // 剪枝之优化搜索顺序——先从大到小枚举半径,再从大到小枚举高度 // 可行性剪枝——只在已确定的可行范围内枚举,范围由单调性放缩得到 for (int r = min(R[u + 1] - 1, (int)sqrt((n - minv[u - 1] - v)/u)); r >= u; -- r) for (int h = min(H[u + 1] - 1, (int)(n - minv[u - 1] - v)/ (r * r)); h >= u; -- h){ int t = (u == m) ? r * r : 0; // 若是最底层,则要把顶部面积加上 R[u] = r, H[u] = h; dfs(u - 1, v + r * r * h, s + 2 * r * h + t); // 这里需注意,回溯时,R[u], H[u]可直接被覆盖,而并没有改变其他的属性,所以无需恢复现场 } }
intmain(){ scanf("%d%d", &n, &m); // 预处理求出前u层总体积,总侧面积的最小值 for (int i = 0; i < m; ++ i){ // 最小时,R和H都等于对应层的编号 minv[i] = minv[i - 1] + i * i * i; mins[i] = mins[i - 1] + 2 * i * i; // 这里是一个小trick,minv[]和mins[]是按蛋糕从上到下计算的每一层的极限体积和侧面积 // 但是最后搜索是从下到上搜索,这样min[]前缀和存的就是还未使用的体积和表面积了 } // 哨兵,由于第m层R和H小于等于第m+1层R和H的值减1,所以这里将m+1层初始化为INF避免影响 R[m + 1] = H[m + 1] = INF; dfs(m, 0, 0); if (ans == INF) ans = 0; printf("%d\n", ans); return0; }