一名苦逼的OIer,想成为ACMer

Iowa_Battleship

洛谷1074 靶形数独

原题链接

终究还是逃不过这题,真的懒得写爆搜。。

在某一行中填数时,可以使用链表以减少无用的遍历,即防止遍历到在该行已经填过的数字。
再开\(3\)个数组,一个记录改格是否填过数,一个记录这一列中哪些数已经存在,一个记录这个格子所在的九宫格里哪些数已经存在。
对于转成九宫格的下标,其实是有公式的,不过我比较懒,就直接用\(if\)判断了。
在搜到的格子分数相对较小的情况下,从小到大填数,相对较大则从大到小填。
而一个比较好优化就是先搜未知量少的行,能有效减少搜索树的枝叶。
另外,这题可以用二进制优化的数独,效率比单纯的爆搜要高,但我太菜了,不会。

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 11;
struct lt {
	int pre, suc;
};
lt H[N][N];
struct dd {
	int id, s;
};
dd nz[N];
bool v[N][N], vl[N][N], vg[4][4][N];
int a[N][N] = {
	{0},
	{0, 6, 6, 6, 6, 6, 6, 6, 6, 6},
	{0, 6, 7, 7, 7, 7, 7, 7, 7, 6},
	{0, 6, 7, 8, 8, 8, 8, 8, 7, 6},
	{0, 6, 7, 8, 9, 9, 9, 8, 7, 6},
	{0, 6, 7, 8, 9, 10, 9, 8, 7, 6},
	{0, 6, 7, 8, 9, 9, 9, 8, 7, 6},
	{0, 6, 7, 8, 8, 8, 8, 8, 7, 6},
	{0, 6, 7, 7, 7, 7, 7, 7, 7, 6},
	{0, 6, 6, 6, 6, 6, 6, 6, 6, 6},
}, ans, s;
inline int re()
{
	int x = 0;
	char c = getchar();
	bool p = 0;
	for (; c < '0' || c > '9'; c = getchar())
		p |= c == '-';
	for (; c >= '0' && c <= '9'; c = getchar())
		x = x * 10 + c - '0';
	return p ? -x : x;
}
void dfs(int x, int y);
inline int maxn(int x, int y) { return x > y ? x : y; }
inline void del(int x, int y) { H[x][H[x][y].pre].suc = H[x][y].suc; H[x][H[x][y].suc].pre = H[x][y].pre; }//删除链表中的某个链节
inline void rec(int x, int y) { H[x][H[x][y].pre].suc = y; H[x][H[x][y].suc].pre = y; }//恢复链表中的某个链节
inline int ch(int x) { return x < 4 ? 1 : x > 6 ? 3 : 2; }//转为就九宫格的下标
bool comp(dd x, dd y) { return x.s > y.s; }
void calc(int x, int y, int i, int xx)
{
	if (vl[y][i] || vg[ch(x)][ch(y)][i])
		return;
	v[x][y] = vl[y][i] = vg[ch(x)][ch(y)][i] = 1;
	del(x, i);
	s += i * a[x][y];
	dfs(xx, y + 1);
	s -= i * a[x][y];
	rec(x, i);
	v[x][y] = vl[y][i] = vg[ch(x)][ch(y)][i] = 0;
}
void dfs(int xx, int y)
{
	if (y > 9)
		++xx, y = 1;
	int i, x = nz[xx].id;
	if (x > 9)
	{
		ans = maxn(ans, s);
		return;
	}
	if (v[x][y])
	{
		dfs(xx, y + 1);
		return;
	}
	if (a[x][y] < 9)//格子分数相对较小的情况下,从小到大填数
		for (i = H[x][0].suc; i <= 9; i = H[x][i].suc)
			calc(x, y, i, xx);
	else//否则从大到小填
		for (i = H[x][10].pre; i; i = H[x][i].pre)
			calc(x, y, i, xx);
}
int main()
{
	int i, j, x;
	for (i = 1; i <= 9; i++)
		for (j = H[i][0].suc = 1, H[i][10].pre = 9; j <= 9; j++)
			H[i][j].pre = j - 1, H[i][j].suc = j + 1;//初始化链表
	for (i = 1, nz[10].id = 10; i <= 9; i++)
		for (j = 1, nz[i].id = i; j <= 9; j++)
		{
			x = re();
			if (!x)
				continue;
			nz[i].s++;
			s += x * a[i][j];
			del(i, x);
			if (vl[j][x] || vg[ch(i)][ch(j)][x])//如果输入的数独就不合法,直接输出-1
				return printf("-1"), 0;
			v[i][j] = vl[j][x] = vg[ch(i)][ch(j)][x] = 1;
		}
	sort(nz + 1, nz + 10, comp);//按未知量的多少对行的搜索顺序进行排序
	dfs(1, 1);
	printf("%d", ans ? ans : -1);
	return 0;
}

posted on 2019-02-17 15:21  Iowa_Battleship  阅读(148)  评论(0编辑  收藏  举报

导航