一名苦逼的OIer,想成为ACMer

Iowa_Battleship

洛谷1092 虫食算

原题链接

这题可以用爆搜或高斯消元,这里我用的是高斯消元。
对于题目中的样例:

ABCED
BDACE
EBBAA

我们可以将其转化为如下的方程组:

{A+B=E+k1B+D=B+k2C+A=B+k3E+C=A+k4D+E=A+k5

ki是因为进位的关系所加上去的数。若该位是进位的,ki=n,若该位是被进位的,ki=1,若既进位有被进位,ki=n1,若都没有则ki=0
将未知数移到一边得(ki看作常数):

{A+BE=k1D=k2C+AB=k3E+CA=k4D+EA=k5

转换为高斯消元的系数矩阵:

[ABCDE|ki11001|k100010|k211100|k310101|k410011|k5]

于是很容易想到一种做法,先搜索ki的值,然后用高斯消元判断是否正确即可,但复杂度为O(2n×n3),显然超时了。
考虑按上述思路逆向去做,将ki也看作未知数,则矩阵转化为:

[ABCDE|k1k2k3k4k511001|1000000010|0100011100|0010010101|0001010011|00001]

进行一遍高斯消元后即可得到如下的关系式:

{A=k12k2+k3k4+2k5B=k1k2+k5C=k2+k4k5D=k2E=k13k2+k3k4+3k5

然后再去搜索ki的值代入关系式判断是否有解即可。
时间复杂度O(n3+2n×n2),虽然依旧很高,但由于在检验ki的值是否可行时,大多数情况都无法达到n2(基本在判断第一个解时就能判断是错的),因此是能通过本题的。
另外,ki的值取决于ki1的值,当ki101时,ki只能为0n,当ki1nn1时,ki只能为1n1,即前一位是否进位关系该位的取值。
所以虽然ki4个取值,但搜索的复杂度仅为2n

#include<cstdio>
#include<cstring>
using namespace std;
const int N = 30;
int X[N][N], K[N][N], V[N], ans[N], n;
bool v[N];
inline char re()
{
	char c = getchar();
	for (; c < 'A' || c > 'Z'; c = getchar());
	return c;
}
inline void sw(int& x, int& y) { int z = x; x = y; y = z; }
inline int jd(int x) { return x < 0 ? -x : x; }
inline int gcd(int x, int y) { return y ? gcd(y, x % y) : x; }
bool judge()
{
	int i, j;
	memset(v, 0, sizeof(v));
	for (i = 1; i <= n; i++)
	{
		for (ans[i] = 0, j = 1; j <= n; j++)
			ans[i] += V[j] * K[i][j];
		if (ans[i] % X[i][i])
			return false;
		ans[i] /= X[i][i];
		if (ans[i] < 0 || ans[i] >= n || v[ans[i]])
			return false;
		v[ans[i]] = 1;
	}
	return true;
}
bool dfs(int x, int y)
{
	V[x] = y;
	if (!(x ^ 1))
		return judge();
	if (dfs(x - 1, 0))
		return true;
	V[x] += n;
	return dfs(x - 1, -1);
}
int main()
{
	int i, j, k, lcn, x, y;
	scanf("%d", &n);
	for (i = 1; i < 3; i++)
		for (j = 1; j <= n; j++)
			X[j][re() - 'A' + 1]++;
	for (i = 1; i <= n; i++)
		X[i][re() - 'A' + 1]--, K[i][i] = 1;
	for (i = 1; i <= n; i++)
	{
		for (k = i, j = i + 1; j <= n; j++)
			if (jd(X[k][i]) < jd(X[j][i]))
				k = j;
		if (k ^ i)
			for (j = 1; j <= n; j++)
				sw(X[k][j], X[i][j]), sw(K[k][j], K[i][j]);
		for (j = 1; j <= n; j++)
			if (X[j][i] && j ^ i)
			{
				lcn = X[j][i] * X[i][i] / gcd(X[j][i], X[i][i]);
				x = lcn / X[j][i]; y = lcn / X[i][i];
				for (k = 1; k <= n; k++)
					X[j][k] = X[j][k] * x - X[i][k] * y, K[j][k] = K[j][k] * x - K[i][k] * y;
			}
	}
	dfs(n, 0);
	for (i = 1; i <= n; i++)
		printf("%d ", ans[i]);
	return 0;
}

posted on   Iowa_Battleship  阅读(249)  评论(0编辑  收藏  举报

编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示