1284: [NOIP2004]虫食算
时间限制: 1 Sec 内存限制: 64 MB
题目描述
所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。来看一个简单的例子:
43#9865#045
+ 8468#6633
———————————
44445506978
其中#号代表被虫子啃掉的数字。
根据算式,我们很容易判断:第一行的两个数字分别是5和3,第二行的数字是5。
现在,我们对问题做两个限制:
首先,我们只考虑加法的虫食算。这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0。
其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用相同的字母表示,不同的数字用不同的字母表示。
如果这个算式是N进制的,我们就取英文字母表午的前N个大写字母来表示这个算式中的0到N-1这N个不同的数字:但是这N个字母并不一定顺序地代表0到N-1)。
输入数据保证N个字母分别至少出现一次。
BADC
+ CBDA
————————–
DCCC
上面的算式是一个4进制的算式。很显然,我们只要让ABCD分别代表0123,便可以让这个式子成立了。
你的任务是,对于给定的N进制加法算式,求出N个不同的字母分别代表的数字,使得该加法算式成立。输入数据保证有且仅有一组解。
输入
输入文件alpha.in包含4行。第一行有一个正整数N(N<=26),后面的3行每行有一个由大写字母组成的字符串,分别代表两个加数以及和。这3个字符串左右两端都没有空格,从高位到低位,并且恰好有N位。
输出
输出文件alpha.out包含一行。在这一行中,应当包含唯一的那组解。解是这样表示的:输出N个数字,分别表示A,B,C……所代表的数字,相邻的两个数字用一个空格隔开,不能有多余的空格。
样例输入
5
ABCED
BDACE
EBBAA
样例输出
1 0 3 4 2
【数据规模】
对于30%的数据,保证有N≤10;
对于50%的数据,保证有N≤15;
对于全部的数据,保证有N≤26。
提示
根据加法法则,可以列出n个方程:
样例分析:
5
ABCED
BDACE
EBBAA
D+E=A+n*d1
E+C+d1=A+n*d2
C+A+d2=B+n*d3
B+D+d3=B+n*d4
A+B+d4=E
<=>
D+E-A=n*d1
E+C-A=n*d2-d1
C+A-B=n*d3-d2
B+D-B=n*d4-d3
A+B-E=-d4
可用高斯消元解出x[] (A~E为未知数)关于d[] 的关系式。
由于d[]只能是0或1,枚举d[]
再check是不是代入得出的x[]是 [0,n) 中的整数,且互不相等。
时间复杂度:O( 2^(n-1) * (n^2) ) ,但由于一遇到不合法的数就退出check,一遇到ans就退出程序,实际复杂度要小很多。
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 26
int n,d[MAXN+10],x[MAXN+10],a[MAXN+10][MAXN+10],g[MAXN+10][MAXN+10];
bool vis[MAXN+10];
char s[5][MAXN+10];
void read()
{
scanf("%d%s%s%s",&n,s[0]+1,s[1]+1,s[2]+1);
memset(a,0,sizeof a);
for(int i=1;i<=n;i++){
a[n-i+1][s[0][i]-'A'+1]++;
a[n-i+1][s[1][i]-'A'+1]++;
a[n-i+1][s[2][i]-'A'+1]--;
}
for(int i=1;i<=n;i++){
if(i!=n)
g[i][i]=n;
if(i!=1)
g[i][i-1]=-1;
}
}
int gcd(int a,int b)
{
int t;
while(b){
t=b;
b=a%t;
a=t;
}
return a;
}
void GJ_elimination(int equ,int var,int &row,int &col)
{
int mx,k;
for(row=col=1;row<=equ&&col<=var;row++,col++){
mx=row;
for(int i=row+1;i<=equ;i++)
if(abs(a[i][col])>abs(a[mx][col]))
mx=i;
if(mx!=row){
swap(a[row],a[mx]);
swap(g[row],g[mx]);
}
if(a[row][col]==0){
row--;
continue;
}
for(int i=1;i<=equ;i++){
if(i==row||a[i][col]==0)
continue;
k=gcd(a[i][col],a[row][col]);
int ta=a[i][col]/k,tb=a[row][col]/k,f=0;
for(int j=var;j>=1;j--){
a[i][j]=a[i][j]*tb-a[row][j]*ta;
f=gcd(f,a[i][j]);
}
for(int j=1;j<n;j++){
g[i][j]=g[i][j]*tb-g[row][j]*ta;
f=gcd(f,g[i][j]);
}
//化简
if(f!=0&&f!=1){
for(int j=var;j>=1;j--)
a[i][j]/=f;
for(int j=1;j<n;j++)
g[i][j]/=f;
}
}
}
}
void Gauss_Jordan(int equ,int var)
{
int row,col;
GJ_elimination(equ,var,row,col);
//GJ_Judge(equ,var,row,col);//题目说有唯一解
}
void print()
{
for(int i=1;i<n;i++)
printf("%d ",x[i]);
printf("%d\n",x[n]);
}
bool check()
{
memset(vis,0,sizeof vis);
for(int i=1;i<=n;i++){
int tmp=0;
for(int j=1;j<n;j++)
tmp+=g[i][j]*d[j];
if(tmp%a[i][i])
return false;
tmp/=a[i][i];
if(tmp<0||tmp>=n||vis[tmp])
return false;
x[i]=tmp;
vis[tmp]=true;
}
return true;
}
void dfs(int u)
{
if(u>=n){
if(check()){
print();
exit(0);
}
return ;
}
d[u]=1; dfs(u+1);
d[u]=0; dfs(u+1);
}
int main()
{
read();
Gauss_Jordan(n,n);
dfs(1);
}
一位大神的blog,内容之详尽,blog写的之好,皓月与萤星啊
再来份搜索的代码,之丑,重写了回来更新:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define MAXN 26
#define MAXNUM 90
int n,alp[MAXNUM+10];
char x[MAXN+10],y[MAXN+10],z[MAXN+10];
bool vis[MAXN+10];
int check(int dep,int i,int j,int r)
{
if(alp[(int)z[dep]]!=-1){
if((i+j+r)%n==alp[(int)z[dep]]) return 1;
return 0;
}
if(vis[(i+j+r)%n]) return 0;
alp[(int)z[dep]]=(i+j+r)%n;
vis[(i+j+r)%n]=true;
return 2;
}
void print()
{
for(int i=0;i<n;i++){
if(i) printf(" ");
printf("%d",alp[i+'A']);
}
}
void dfs(int dep,int r)
{
if(dep>n){
if(!r){
print();
exit(0);
}
return ;
}
if(alp[(int)x[dep]]==-1){
for(int i=n-1;i>=0;i--)
if(!vis[i]){
alp[(int)x[dep]]=i,vis[i]=true;
if(alp[(int)y[dep]]==-1){
for(int j=n-1;j>=0;j--)
if(!vis[j]){
alp[(int)y[dep]]=j,vis[j]=true;
int cc=check(dep,i,j,r);
if(cc){
dfs(dep+1,(i+j+r)/n);
if(cc==2)
alp[(int)z[dep]]=-1,vis[(i+j+r)%n]=false;
}
alp[(int)y[dep]]=-1,vis[j]=false;
}
}
else{
int cc=check(dep,i,alp[(int)y[dep]],r);
if(cc){
dfs(dep+1,(i+alp[(int)y[dep]]+r)/n);
if(cc==2)
alp[(int)z[dep]]=-1,vis[(i+alp[(int)y[dep]]+r)%n]=false;
}
}
alp[(int)x[dep]]=-1,vis[i]=false;
}
}
else{
if(alp[(int)y[dep]]==-1){
for(int j=0;j<n;j++)
if(!vis[j]){
alp[(int)y[dep]]=j,vis[j]=true;
int cc=check(dep,alp[(int)x[dep]],j,r);
if(cc){
dfs(dep+1,(alp[(int)x[dep]]+j+r)/n);
if(cc==2)
alp[(int)z[dep]]=-1,vis[(alp[(int)x[dep]]+j+r)%n]=false;
}
alp[(int)y[dep]]=-1,vis[j]=false;
}
}
else{
int cc=check(dep,alp[(int)x[dep]],alp[(int)y[dep]],r);
if(cc){
dfs(dep+1,(alp[(int)x[dep]]+alp[(int)y[dep]]+r)/n);
if(cc==2)
alp[(int)z[dep]]=-1,vis[(alp[(int)x[dep]]+alp[(int)y[dep]]+r)%n]=false;
}
}
}
}
void turn_around(char *s)
{
char t[MAXN+10];
for(int i=0;i<n;i++)
t[i]=s[i];
memset(s,0,sizeof(s));
for(int i=n-1,j=1;i>=0;i--,j++)
s[j]=t[i];
}
int main()
{
scanf("%d%s%s%s",&n,x,y,z);
turn_around(x);
turn_around(y);
turn_around(z);
memset(alp,-1,sizeof(alp));
dfs(1,0);
return 0;
}