NOIP提高组1998进制位题解

题解又双叒叕来了!

告诉大家一个有用的东西:

若不会有错,则进制=n,且数值都是连续的

因为:

1必须有(不然十位搞鬼)

然后就:

1 1 2 2必须有,其它数字以此类推

主体思路:DFS

一看到题,有点蒙,大致思考了以后,我想起了以前做过的NOIP2004年的虫食算鬼畜竖式题

于是,我构起了这道题的大致框架:将矩阵分解成一个个竖式(在程序中有Size个竖式),并对每个字母所对应的数进行枚举,最后写出答案。

在做程序中,需要思考以下几个问题:

(一)进制的枚举

最开始我以为进制是在M(不重复字母个数),和N-1(第一行的字母总数)之间,后来发现两者的值一直相同,这个问题就引刃而解了。想多了

(二)每个字母所对应的数的枚举

太简单了!(FJJ说的),这个就是本程序搜索的主体,本蒟蒻认为只要是学过DFS的人都会——定义一个递归函数,函数有一个参量,就是矩阵(输入的那个)的第一行的每一列(从2到N),指的是每个字母,在递归函数中放一个循环,从0循环到Top-1(注意是从0开始,不是1,Top指的是Top进制),枚举每个字母所代表的数——解决了!

在这里我还用了STL里面的map,用在这里是将一个字符串作为下标来存储布尔值或整形

(三)如何将矩阵做成竖式

将整个矩阵扫一遍,用一个数组存现在所在的那一列的第一个字母(加数一),用另一个字母存储所在的那一行的第一个字母(加数二),用第三个数组存储那一行那一列的字母(可能是字符串),这个问题也就解决了。

(四)如何判断这个枚举是正确的(前方高能,呼叫博主)

首先,我们要加刚才准备好的竖式按枚举出来的答案一一转换为数字:

先推荐一种数据结构转换成数字的代码然后就是就是转换了,我的思路是用一个空数组计算得到的两个加数的和(这里用到了高精度的思想),在和原来的和进行对比,一样则通过,不一样则表示枚举有问题(要对每一个竖式进行检验)而这两个函数的入口应该放在深搜函数的开始处,并且需要特判是否枚举完每个字母

(五)输出

不想讲了,它叫我们怎么输出就怎么输出

总结:

时间复杂度:O(1)(进制枚举)*O(N)(字母枚举)*O(N)(每个字母所对应的数字的枚举)*O(N)(每个扫描竖式)*O(N)(每个竖式的确认)=O(N^4)——不会超时(N<=9)——不需要剪枝了(我其实是懒得剪)。

有一点要注意:每个竖式转换成数字时数字竖式数组要时时清空,要不然就会出现WA的情况。

上代码

#include <bits/stdc++.h>
using namespace std;

struct Unsigned{
    int V[99];
    int Size;
};

string Num1[99],Num2[99],Sum[99];
Unsigned Queue1[99],Queue2[99];
map<char,bool> Visited;
map<char,int> Judge2;
Unsigned Queue3[99];
string Map[99][99];
int ACanswer[99];
Unsigned Judge3;
bool Used[99];
int N,Size,M;
int Top;
bool Judge(void){
    for(int i=1;i<=Size;i++){
        memset(&Judge3,0,sizeof(Judge3));
        Judge3.Size=max(Queue1[i].Size,Queue2[i].Size);
        for(int j=0;j<Judge3.Size;j++){
            Judge3.V[j]=Judge3.V[j]+Queue1[i].V[j]+Queue2[i].V[j];
            Judge3.V[j+1]=Judge3.V[j]/Top;
            Judge3.V[j]=Judge3.V[j]%Top;
        }
        if(Judge3.V[Judge3.Size]>0)
            Judge3.Size++;
        if(Judge3.Size!=Queue3[i].Size)
            return false;
        for(int j=0;j<Judge3.Size;j++)
            if(Judge3.V[j]!=Queue3[i].V[j])
                return false;
    }
    return true;
}
void Print(void){
    for(int i=2;i<=N;i++){
        int Out=Judge2[Map[1][i][0]];
        cout<<Map[1][i]<<"="<<Out<<" ";
    }
    cout<<endl;
    cout<<Top<<endl;
    exit(0);
}
void Made(void){
    memset(&Queue1,0,sizeof(Queue1));
    memset(&Queue2,0,sizeof(Queue2));
    memset(&Queue3,0,sizeof(Queue3));
    for(int i=1;i<=Size;i++){
        for(int j=Num1[i].size()-1;j>=0;j--)
            Queue1[i].V[Queue1[i].Size++]=Judge2[Num1[i][j]];
        for(int j=Num2[i].size()-1;j>=0;j--)
            Queue2[i].V[Queue2[i].Size++]=Judge2[Num2[i][j]];
        for(int j=Sum[i].size()-1;j>=0;j--)
            Queue3[i].V[Queue3[i].Size++]=Judge2[Sum[i][j]];
    }
}
void Depth_First_Search(int Last){
    if(Last==N+1){Made();
        if(Judge()==true)
            Print();
        return;
    }
    for(int i=0;i<Top;i++){
        if(Used[i]==false){
            if(Judge2[Map[1][Last][0]]==-1){ 
                Used[i]=true;
                Judge2[Map[1][Last][0]]=i;
                Depth_First_Search(Last+1);
                Judge2[Map[1][Last][0]]=-1;
                Used[i]=false;
            }
        }
    }
}
int main(void){
    cin>>N;
    for(int i=1;i<=N;i++)
        for(int j=1;j<=N;j++)
            cin>>Map[i][j];
    for(int i=2;i<=N;i++){
        for(int j=2;j<=N;j++){
            for(int k=0;k<Map[i][j].size();k++)
                if(Visited[Map[i][j][k]]==false)
                    Visited[Map[i][j][k]]=true,M++;
            Sum[++Size]=Map[i][j];
            Num1[Size]=Map[i][1];
            Num2[Size]=Map[1][j];
        }
    }
    for(int i=2;i<=N;i++)
        Judge2[Map[i][1][0]]=-1;
    for(Top=N-1;Top<=M;Top++){
        Depth_First_Search(2);
    }
        cout<<"ERROR!"<<endl;
    return 0;
}    

你学废了吗?

学废了扣1,没学废扣眼珠子

最后,请大家:

小蒟蒻ΩωΩ I AK IOI.
原文地址:https://www.cnblogs.com/20070618hyz/p/12829961.html