搜索 题目

1|0Sudoku(9*9)


link1:数据强度弱
link2:数据强度强

两个优化:

  1. 按照人类直觉,可以填的数越少的位置越先填
  2. 使用二进制数字记录一行、列、宫中哪些数字用过,不使用数组,常数优化
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std ;
typedef long long ll ;
const int N = 1000010 ;
const int M = (1 << 11) ;
bool flag ;
int n, T, tot ;
char s[20][20] ;
int row[20], col[20], blk[20] ; // block -> int
int belong[20][20] ;
int cnt[M], dig[M] ;
void pre() {
for (int i = 0; i <= 2; i++)
for (int j = 0; j <= 2; j++) {
belong[i][j] = 0 ;
belong[i][j + 3] = 1 ;
belong[i][j + 6] = 2 ;
belong[i + 3][j] = 3 ;
belong[i + 3][j + 3] = 4 ;
belong[i + 3][j + 6] = 5 ;
belong[i + 6][j] = 6 ;
belong[i + 6][j + 3] = 7 ;
belong[i + 6][j + 6] = 8 ;
}
for (int i = 0; i <= (1 << 9); i++)
for (int j = i; j; j -= j & -j) cnt[i]++ ;
for (int i = 0; i <= 9; i++) dig[1 << i] = i ;
}
void init() {
tot = 0 ;
for (int i = 0; i < n; i++) row[i] = col[i] = blk[i] = (1 << 9) - 1 ;
}
void flip(int x, int y, int z) {
row[x] ^= (1 << z) ;
col[y] ^= (1 << z) ;
blk[belong[x][y]] ^= (1 << z) ;
}
int dfs(int step) {
if (step == 0) {
flag = true ;
return 1 ;
}
int ans = (1 << 9) - 1, dx, dy ;
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
if (s[i][j] == '0') {
int t = row[i] & col[j] & blk[belong[i][j]] ;
if (cnt[t] < cnt[ans]) {
ans = t ;
dx = i, dy = j ;
}
}
while (ans) {
int t = ans & -ans ; ans -= ans & -ans ; t = dig[t] ;
s[dx][dy] = t + '1' ;
flip(dx, dy, t) ;
if (dfs(step - 1)) return 1 ;
s[dx][dy] = '0' ;
flip(dx, dy, t) ;
}
return 0 ;
}
int main() {
n = 9 ;
scanf("%d", &T) ;
pre() ;
while (T--) {
init() ;
for (int i = 0; i < n; i++) scanf("%s", s[i]) ;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (s[i][j] == '0') tot++ ;
else flip(i, j, s[i][j] - '1') ;
dfs(tot) ;
for (int i = 0; i < n; i++) printf("%s\n" , s[i]) ;
}
}

2|0Sticks


link

64根棒子 直接搜索时间复杂度太高,需要剪枝优化

  1. 搜索顺序:对于每个 stick 尽量先把大个的部分加进来,不行再用小的
  2. 冗余:(确保AB和BA不出现):
    2.1 记录上次棒子取出位置,只取比他小的不取大的(跟搜索)
    2.2 因为棒子长度可能相同,要记录上次失败时使用的棒子长度,再次枚举时不使用这个长度的
  3. 可行性剪枝 (确保失败的不找了)
    ·3.1 如果一次枚举是从空的开始的,而且失败了,那么直接放弃整个方案,因为这个失败棒放哪都是等效的,都注定失败
    ·3.2 如果一次枚举加入一根棒子,一段完成,但是之后的枚举失败了,同样放弃整个方案
    因为后面放的都是用较小的棒子拼起来塞进去,根据贪心,相比于多个小棒子,大棒子肯定更优
    大棒子都失败了,小棒子更不可能成功
  4. 最优性剪枝:不需要,只需要答案从小往大就行
  5. 记忆化:不需要,没有明确的状态可言
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std ;
typedef long long ll ;
const int N = 1000010 ;
int n, L, sum, val, cnt ;
int a[N], v[N] ;
bool cmp(int a, int b) {
return a > b ;
}
void init() {
sum = val = 0 ;
}
int dfs(int num, int len, int lst) {
if (num > cnt) return 1 ;
if (len == L) return dfs(num + 1, 0, 1) ;
int fail = 0 ;
for (int i = lst; i <= n; i++)
if (!v[i] && len + a[i] <= L && fail != a[i]) {
v[i] = 1 ;
if (dfs(num, len + a[i], i + 1)) return true ;
fail = a[i] ;
v[i] = 0 ;
if (len == 0 || len + a[i] == L) return false ;
}
return false;
}
int main() {
while(scanf("%d", &n) != EOF && n) {
init() ;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]), sum += a[i], val = max(val, a[i]) ;
sort(a + 1, a + n + 1, cmp) ;
for (L = val; L <= sum; L++)
if (sum % L == 0) {
cnt = sum / L ;
memset(v, 0, sizeof(v)) ;
if (dfs(1, 0, 1)) {
printf("%d\n", L) ;
break ;
}
}
}
}

__EOF__

作  者哈奇莱特
出  处https://www.cnblogs.com/lighthqg/p/17689572.html
关于博主:这个人很懒 什么也没有留下
版权声明:未获得本人同意请勿随意转载
声援博主:制作不易 点个赞吧

posted @   哈奇莱特  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
0
0
关注
跳至底部
点击右上角即可分享
微信分享提示