【高斯消元】[NOIP2004]虫食算 —— 正解
题目描述:所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。来看一个简单的例子:
其中a号代表被虫子啃掉的数字。
根据算式,我们很容易判断:第一行的两个数字分别是5和3,第二行的数字是5。
现在,我们对问题做两个限制:
首先,我们只考虑加法的虫食算。这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0。
其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用相同的字母表示,不同的数字用不同的字母表示。
如果这个算式是N进制的,我们就取英文字母表午的前N个大写字母来表示这个算式中的0到N-1这N个不同的数字:但是这N个字母并不一定顺序地代表0到N-1)。
输入数据保证N个字母分别至少出现一次。
上面的算式是一个4进制的算式。很显然,我们只要让ABCD分别代表0123,便可以让这个式子成立了。
你的任务是,对于给定的N进制加法算式,求出N个不同的字母分别代表的数字,使得该加法算式成立。输入数据保证有且仅有一组解。
样例输入:
5
ABCED
BDACE
EBBAA
样例输出:
1 0 3 4 2
【数据规模】
对于30%的数据,保证有N≤10;
对于50%的数据,保证有N≤15;
对于全部的数据,保证有N≤26。
下面题解开始:
如果我们直接暴搜显然是不行的复杂度有
那么我们可以发现因为有
然后对于每一列我们可以得到一个方程我们令
那么我们现在列出来的矩阵就是长得这个样子(样例的矩阵)
这个时候只需要判断三个不合法的情况
- 当前的整数有没有被使用过
- 当前的数是不是个整数
当前的数字在不在给定的范围内
因为大部分的情况下枚举完成带入时如果不合法那么第一个
A 很容易就无法被整除了所以实际的复杂度更加接近于O(2n−1n)
开始写吧!!!
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
typedef long long LL;
const int MAXN = 26;
int _abs(int u){return u>0?u:-u;}
int gcd(int a, int b){
int c;
while(b){
c = a % b;
a = b;
b = c;
}
return a;
}
int Ma[MAXN+10][MAXN+10], g[MAXN+10][MAXN+10], d[MAXN+10], n, x[MAXN+10];
bool vis[MAXN+10];
bool check(){
memset(vis, 0, sizeof vis);
for(int i=1;i<=n;i++){
int sum = 0;
for(int j=2;j<=n;j++)
sum += d[j] * g[i][j];
if(sum % Ma[i][i] != 0) return false;
sum /= Ma[i][i];
if(sum < 0 || sum >= n) return false;
if(vis[sum]) return false;
vis[x[i] = sum] = true;
}
return true;
}
void dfs(int u){
if(u == 1){
if(check()){
for(int i=1;i<n;i++)
printf("%d ", x[i]);
printf("%d\n", x[n]);
exit(0);
}
return ;
}
d[u] = 0; dfs(u-1);
d[u] = 1; dfs(u-1);
}
void solve(int n){
int col, row;
for(col=row=1;row<=n&&col<=n;row++,col++){
int Maxp = row;
for(int i=row+1;i<=n;i++)
if(_abs(Ma[i][col]) > _abs(Ma[Maxp][col]))
Maxp = i;
if(Maxp != row){
swap(Ma[Maxp], Ma[row]);
swap(g[Maxp], g[row]);
}
for(int i=1;i<=n;i++){
if(i != row && Ma[i][col] != 0){
int lcm = Ma[i][col] * Ma[row][col] / gcd(Ma[i][col], Ma[row][col]);
int d1 = lcm / Ma[i][col], d2 = lcm / Ma[row][col];
for(int j=1;j<=n;j++){
Ma[i][j] = Ma[i][j] * d1 - Ma[row][j] * d2;
g[i][j] = g[i][j] * d1 - g[row][j] * d2;
}
}
}
}
}
char s1[MAXN+10], s2[MAXN+10], s3[MAXN+10];
int main(){
scanf("%d", &n);
scanf("%s%s%s", s1+1, s2+1, s3+1);
memset(Ma, 0, sizeof Ma);
for(int i=1;i<=n;i++){
++Ma[i][s1[i]-'A'+1];
++Ma[i][s2[i]-'A'+1];
--Ma[i][s3[i]-'A'+1];
g[i][i] = n, g[i][i+1] = -1;
}
solve(n);
dfs(n);
return 0;
}
另有搜索算法请见搜索大法好