[NOIP2004]虫食算 - 高斯消元|搜索

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;
}
posted @ 2016-01-30 17:45  KatarinaYuan  阅读(299)  评论(0编辑  收藏  举报