noip2009 靶形数独 (代码还算不丑)

核心是用位运算简化操作,并根据每行的“0”的数量确定枚举的顺序

详见代码及代码注释

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
 
int target[10][10]={
	{6,6,6,6,6,6,6,6,6},
	{6,7,7,7,7,7,7,7,6},
	{6,7,8,8,8,8,8,7,6},
	{6,7,8,9,9,9,8,7,6},
	{6,7,8,9,10,9,8,7,6},
	{6,7,8,9,9,9,8,7,6},
	{6,7,8,8,8,8,8,7,6},
	{6,7,7,7,7,7,7,7,6},
	{6,6,6,6,6,6,6,6,6}};

int a[10][10];
int lg2[1<<10], line[10];
	//lg2[i]:快速算对数(用于找到拿出来的那个数字是几)
	//line[i]:第i行有那些位置填上了 
int row[10], col[10], grid[10];
	//分别记录行/列/块中的已选元素 
int h[10], st[10];
	//st数组:搜索顺序 
bool flag = 0;
int Maxsum = 0;
const int allset = (1 << 9) - 1;

void init() {
	for(int i = 0; i < 10; i++)
		lg2[1<<i] = i;
}

int lowbit(int x) {
	return x&(-x);
}

void add(int x, int y, int pos, int p) {
	row[x] |= p, col[y] |= p, grid[pos] |= p;
}

void del(int x, int y, int pos, int p) {
	row[x] -= p, col[y] -= p, grid[pos] -= p;
}

bool cmp(int x, int y) {
	return h[x] < h[y];
}

void dfs(int c, int sum) {
	if(c == 9) {
		if(Maxsum < sum)
			Maxsum = sum;
		flag = true;	return; 
	}
	int x = st[c], setx = allset - line[x];
	//setx本行还能填什么数字 
	if(!setx) {
		dfs(c+1, sum);
		return;
	}	//本行已经搜索结束; 
	int sety = lowbit(setx), y = lg2[sety];
	//sety : 待处理的最后一位数
	//y:y的具体位置; 
	line[x] |= sety;
	int pos = x/3*3 + y/3;	//所属于哪个块 
	int num = allset - (row[x]|col[y]|grid[pos]);
		//num:还能填哪些数 
	while(num) {
		int p = lowbit(num);
		num -= p;
		//每次去掉搜索过的数; 
		a[x][y] = lg2[p] + 1; 
		add(x, y, pos, p);
		if(setx == sety)	//如果本行只能填一个数字 
			dfs(c+1, sum+a[x][y]*target[x][y]);
		else
			dfs(c, sum+a[x][y]*target[x][y]);
		a[x][y] = 0;
		del(x, y, pos, p);
	}
	line[x] -= sety;
}	//每次递归搜索的是一个点,而不是一行; 

int main() {
	init();
	for(int i = 0; i < 9; i++)
		for(int j = 0; j < 9; j++) {
			scanf("%d", &a[i][j]);
			if(a[i][j]) {
				int num = a[i][j];
				int pos = i/3*3 + j/3;
				add(i, j, pos, 1<<num-1);
				line[i] |= 1 << j;
				Maxsum += num*target[i][j];
			}	else	h[i]++;
		}
	for(int i = 0; i < 9; i++)
		st[i] = i; 
	sort(st, st+9, cmp);
	dfs(0, Maxsum);
	if(flag)
		printf("%d\n", Maxsum);
	else
		puts("-1");
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值