一名苦逼的OIer,想成为ACMer

Iowa_Battleship

洛谷1549 棋盘问题(2)

原题链接

爆搜题。
先用线性筛预处理出素数,并直接暴力循环预处理出那些数和哪些数之间能拼成素数,当然可以再疯狂点,直接预处理出每两个数可以和哪些数拼成素数(反正\(n\)小,随你预处理)。
然后爆搜的时候注意搜索顺序,先搜第一行第一列,以保证第一行第一列之和最小。
然而普通的搜索顺序搜除去第一行第一列剩下的部分会有一个问题,即从小到大搜会导致\(n = 9\)很慢,若从大到小搜会导致\(n = 7\)很慢。
我的程序从大到小搜的话,\(n = 7\)需要\(1s\)多点,其它秒出,不过经测试开了\(O2\)在本地或是洛谷\(IDE\)上都能在\(0.9s\)内过。
不过我还不够满意,既然不能一直从小到大,也不能一直从大到小,那就折中一下呗,上随机数。
测试了\(5\)个随机数种子,总算让我找到一个能够使得程序秒出\(1\sim 10\)所有数据的了(甚至可以出\(11,12\))。
这样总算是真正的\(A\)了此题了。
另外,我发现这个随机数种子仅是使得我本地能过,在洛谷\(IDE\)上却无法出\(9\)的数据,可能是因为操作系统不同导致相同随机数种子的结果不同,所以若真要搞过,还是\(time(NULL)\)看人品吧。。

#include<cstdio>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N = 13;
const int M = N * N;
const int K = M << 1;
int a[N][N], p[M][M], pr[K], L[M], P[M][M][M], PL[M][M], n, l, o;
bool v[K], vis[M], pi[M][M];
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;
}
inline void ri(int &x, int &y)
{
	y++;
	if (y > n)
	{
		y = 1;
		x++;
	}
}
inline void dw(int &x, int &y)
{
	x++;
	if (x > n)
		x = y = 2;
}
bool dfs(int x, int y)
{
	if (x > n)
		return true;
	int i, le = a[x][y - 1], u = a[x - 1][y], xx = x, yy = y, g;
	if (!(x ^ 1) && y ^ 1)//搜第一行
	{
		ri(xx, yy);
		for (i = 1; i <= L[le]; i++)
			if (!vis[g = p[le][i]])
			{
				a[x][y] = g;
				vis[g] = 1;
				if (dfs(xx, yy))
					return true;
				vis[g] = 0;
				a[x][y] = 0;
			}
	}
	else
		if (x ^ 1 && !(y ^ 1))//搜第一列
		{
			dw(xx, yy);
			for (i = 1; i <= L[u]; i++)
				if (!vis[g = p[u][i]])
				{
					a[x][y] = g;
					vis[g] = 1;
					if (dfs(xx, yy))
						return true;
					vis[g] = 0;
					a[x][y] = 0;
				}
		}
		else
		{
			ri(xx, yy);
			if (!(yy ^ 1))
				yy++;
			if (rand() & 1)//随机数折中
				for (i = PL[le][u]; i; i--)//从大到小搜
				{
					g = P[le][u][i];
					if (!vis[g])
					{
						a[x][y] = g;
						vis[g] = 1;
						if (dfs(xx, yy))
							return true;
						vis[g] = 0;
						a[x][y] = 0;
					}
				}
			else
				for (i = 1; i <= PL[le][u]; i++)//从小到大搜
				{
					g = P[le][u][i];
					if (!vis[g])
					{
						a[x][y] = g;
						vis[g] = 1;
						if (dfs(xx, yy))
							return true;
						vis[g] = 0;
						a[x][y] = 0;
					}
				}
		}
	return false;
}
int main()
{
	srand(9982123);//仅本地能过,洛谷IDE无法过9的数据,不行还是上time(NULL)吧
	int m, i, j, k, x;
	n = re();
	if (!(n ^ 1))//特判掉1
	{
		printf("NO");
		return 0;
	}
	o = n * n;
	m = o << 1;
	v[0] = v[1] = 1;
	for (i = 2; i <= m; i++)//线性筛素数
	{
		if (!v[i])
			pr[++l] = i;
		for (j = 1; j <= l; j++)
		{
			if (i * pr[j] > m)
				break;
			v[i * pr[j]] = 1;
			if (!(i % pr[j]))
				break;
		}
	}
	for (i = 1; i < o; i++)
		for (j = i + 1; j <= o; j++)//预处理每个数能和哪些数的和是素数
			if (!v[i + j])
			{
				p[i][++L[i]] = j;
				p[j][++L[j]] = i;
				pi[i][j] = pi[j][i] = 1;
			}
	for (i = 1; i <= o; i++)
		sort(p[i] + 1, p[i] + L[i] + 1);//排序
	for (i = 1; i < o; i++)//预处理每两个数能和哪些数拼成素数
		for (j = i + 1, m = L[i]; j <= o; j++)
			for (k = 1; k <= m; k++)
				if (pi[x = p[i][k]][j])
					P[i][j][++PL[i][j]] = P[j][i][++PL[j][i]] = x;
	a[1][1] = 1;
	vis[1] = 1;
	if (dfs(1, 2))
		for (i = 1; i <= n; i++, printf("\n"))
			for (j = 1; j <= n; j++)
				printf("%d ", a[i][j]);
	else
		printf("NO");
	return 0;
}

posted on 2018-10-26 09:13  Iowa_Battleship  阅读(141)  评论(0编辑  收藏  举报

导航