P2447题解
吐槽一句:SDOI2010 真的魔幻……共计 12 题,上能外星捉虫,下能帮猪打牌(逃
Step1 审题 + 题意转化
由于“昆虫点足机”返回的是虫子腿总数
其中
考虑到在正整数的情况下,模
众所周知, 同余方程组一般情况下比较棘手,那么我们有没有可能将她转化为普通的方程组呢?我们知道,这里的每一条方程中所有参数(如
总而言之,我们就成功的将题意转化为了:
- 求解异或方程组
- 输出最少用的方程数
(另外多嘴说一句:为什么要转化为异或方程组呢?因为这样我们就可以用 C++ 中的 std::bitset 与一些二进制优化的方法极大地减小我们的常数 (简称高级卡常),甚至可以做到
Step2 解题思路
转化为异或方程组之后我们又能对她进行什么操作呢?我们知道,异或运算是符合交换律和结合律,所以可以使用类似高斯消元法的方法解决这个异或方程组。具体来说,我们可以在每一次选取一条方程消元时将方程间的加减消元改为异或消元,并且由于
解决了如何求解这个方程组,接下来我们就要解决如何求出最少的方程使用数。实际上,我们可以在每一次消元寻找该元系数非零时找一个编号最小的(即输入顺序最靠前的),然后更新答案取最大值。
分析一下复杂度。首先空间复杂度自然是存增广矩阵的复杂度 显然是
考虑一下用 std::bitset 优化大常数。我们知道 bitset 的异或操作是 bitset 数组来表示增广矩阵,总的时间复杂度就是
Step3 码
嗯?你为什么露出这么期待的表情?
那么有了思路就可以开始写代码了。实际上此题对码力的要求并没有看猪打牌这一题这么高,有了思路基本就可以写出来了。
AC 代码:
#include <iostream>
#include <cstdio>
#include <bitset>
#include <cctype>
// 使用宏定义使程序更清晰(虽然也没有清晰多少)
#define ALIEN "?y7M#"
#define EARTH "Earth"
char buffer[1010];
std::bitset<1010> matrix[2010]; // matrix[1~n]:增广矩阵,0 位置为常数
int GaussElimination(int n, int m) // n 为未知数个数,m 为方程个数,返回 0 表示多解,否则返回最少用到的方程数
{
int ans = -1; // 方程使用数
for (int i = 1; i <= n; i++) // 循环消去第 i 个元
{
int cur = i;
while (cur <= m && !matrix[cur].test(i))
cur++;
if (cur > m) // 第 i 个元的所有系数均为 0,有多解
return 0;
ans = std::max(ans, cur); // 更新 ans
if (cur != i)
swap(matrix[cur], matrix[i]); // 交换方程
for (int j = 1; j <= m; j++)
if (i != j && matrix[j].test(i)) // 可以消元
matrix[j] ^= matrix[i]; // 异或消元
}
return ans;
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1, res; i <= m; i++)
{
scanf("%s%d", buffer, &res);
for (int j = 0; j < n; j++)
matrix[i].set(j + 1, buffer[j] == '1'); // 增广矩阵赋值系数
matrix[i].set(0, res); // 常数
}
int ret = GaussElimination(n, m); // 高斯消元
if (ret)
{
printf("%d\n", ret);
for (int i = 1; i <= n; i++)
printf("%s\n", matrix[i].test(0) ? ALIEN : EARTH);
}
else printf("Cannot Determine\n");
return 0;
}