• P1013 [NOIP1998 提高组] 进制位


    题目链接

    [NOIP1998 提高组] 进制位 - 洛谷

    题目描述

    著名科学家卢斯为了检查学生对进位制的理解,他给出了如下的一张加法表,表中的字母代表数字。 例如:

    1. + L K V E
    2. L L K V E
    3. K K V E KL
    4. V V E KL KK
    5. E E KL KK KV

    其含义为:

    L+L=L,L+K=K,L+V=V,L+E=E

    K+L=K,K+K=V,K+V=E,K+E=KL

    E+E=KV

    根据这些规则可推导出:L=0,K=1,V=2,E=3。

    同时可以确定该表表示的是 4 进制加法。

    输入格式

    第一行一个整数 nn (3 <= n <= 9)表示行数。

    以下 n行,每行包括 n 个字符串,每个字符串间用空格隔开。

    这里的数据范围大家可以到原题看,因为数据范围特别长,我复制的时候到这里粘贴有一串乱码,所以没有写。

    保证至多有一组解。

    输出格式

    第一行输出各个字母表示什么数,格式如:L=0 K=1 ⋯ 按给出的字母顺序排序。不同字母必须代表不同数字。

    第二行输出加法运算是几进制的。

    若不可能组成加法表,则应输出 ERROR!

    输入输出样例

    输入

    5
    + L K V E
    L L K V E
    K K V E KL
    V V E KL KK
    E E KL KK KV
    

    输出 

    L=0 K=1 V=2 E=3
    

    题解

    著名科学家卢斯为了检查学生对进位制的理解,他给出了如下的一张加法表,表中的字母代表数字。 例如:
    +. L K V E
    L L K V E
    K K V E KL
    V V E KL KK
    E E KL KK KV
    其含义为:

    L+L=L,L+K=K,L+V=V,L+E=E
    K+L=K,K+K=V,K+V=E,K+E=K

    …… E+E=KV

    根据这些规则可推导出:L=0,K=1,V=2,E=3
    同时可以确定该表表示的是4进制加法

    输入 n(n≤9)表示行数。

    以下n行,每行包括n个字符串,每个字串间用空格隔开。(字串仅有一个为‘+’号,其它都由大写字母组成)

    输出 ① 各个字母表示什么数,格式如:L=0,K=1,……按给出的字母顺序。

    ② 加法运算是几进制的。

    ③ 若不可能组成加法表,则应输出“ERROR!”
    解题:

    1·字符串与

    :
    首先,map的申明方式:
    map name

    • 是一个键值对key——value的映射。实现是由内部的一棵以key为关键码的红黑树。
    • 而key和value是任意类型,其中key必须定义“小于号”运算符
    • 例如:
    1. #### map vis;
    2. #### map hash;
    3. #### map,vector > test
    4. 所以当我们遇到字符串类型的问题时,便可以用map作hash表,建立字符型(char,string……)key到整型(intlong long ……)value的映射,但平衡树(logn)略慢于hash函数实现的hash表

    相关函数:

    1. size/empty/clear/begin/end (元素个数,判空,清空,首(迭代器),尾(迭代器))
    2. //迭代器都关系二元组pair
    3. insert/erase(插入,删除)(迭代器)
    4. find 对于 name.find(x):表示在变量名为name的map中查找key为x的二元组,若不存在·,返回name.end() //(logn)
    5. name[key] 返回key映射的value
    6. //借此可以方便的获得key对应的value,并可以进行赋值(使用之前先用find检查存在性)

    2·解题部分


    首先最靠谱的解法:先将0,1判断出来,0是和等于某数本身的,1是两位数的十位,然后推出其他数字
    (比如找到加数为1的那行,得到若干形如x+1=y的式子,于是连一条从x到y的边,由于我们知道0是那个数字,最后就可以依据边的连接关系推出所有字母…… 最后再检验检验,错了就输出 ERROR! 即可。)
    并判断对不对……


    思考在十进制下,0+9=9,该行无二位数;
    1+9=10,1+8=9,该行有一个二进制数;
    2+9=11,2+8=10,2+7=9,该行有两个二进制数; 我们可以得出

    (1) 对于任意字母L的一行,两位数个数等于L的值;

    证明,设L=x,设对应某一列的值为y,进制为z,则有:

    x+y=x+(z-(z-y))=x+z-(z-y)=z+(x-(z-y))x+y=x+(z−(z−y))=x+z−(z−y)=z+(x−(z−y))

    因为z加上一个大于0的数一定是一个两位数,换过来,如果要z+(x-(z-y))是一个两位数,则x-(z-y)>=0,所以x-(z-y)=0时恰好取得最后一个两位数,也正好对应了x的值。

    • 另外,特别观察对于z本身是不是两位数的问题:
      (其实本题n<=9并不需要想这么多)
      例如z=4,如题四个数分别是0,1,2,3,此时,0+3=3(x=0,y=3,z=4),1+3=10(x=1,y=3,z=4),发现满足上述性质
      这样我们便有了计算每个字母是多少的方法; 那么如何判断进制?
      严肃的问题来了。

    (2)对于给出数据的合理性

    我们发现,题目并没保证给出的数据都是像样例这样整齐漂亮而有规律的,于是我们面临了几个情况:

    1. 是否满足进制内的数都有?
    2. 某一字母是否可能超出进制?

    ->我们所找的两位数的存在问题
    对于这两个问题,我们先探究加法表的存在性问题:
    首先题目隐藏信息:

    	 进制一定大于最大字母(不然就出锅了)
    

    所以我们就获得了条件:数位最高为2,十位最大为1
    (因为有两位数所以必定有1,有1所以0~n-2必定都有 (下方证明)
    这样就**满足进制内的数都有,并且不会有某一字母超出进制 **
    这就好办了,整理一下现在我们拥有的信息:

    • 有n行n列表示n-1个个位数(n<=9)(第1行第1列都是表头)

    • 一定是能加出两位数来的:

      1. 还是先举例子再证明->n=4来列表
      2. 如果表中没有两位数
      3. + 0 1 2
      4. 0 0 1 2
      5. 1 1 2 3
      6. 2 2 3 4
      7. 明显出现了不存在的字母
      8. 那假如是设进制z=4呢?对于z=4并且没有两位数的表:
      9. + 0 1 2
      10. 0 0 1 2
      11. 1 1 2 3
      12. 2 2 3 4
      13. 明显表中不但不得不出现4,但4已经应该进位,而且还是存在
      14. 表头中不存在的字母的情况
      15. 下面规范的证明一下:
      16. 设在表n中,最大个位数X,进制z
      17. 设表中无两位数
      18. X < z/2 此时有(X+X)作为表中最大数位个位数且小于
      19. 进制z,于假设最大个位数X冲突,所以表中无两位数的假设不
      20. 成立。
    • 进制z=n-1

      1. 由上面的证明的第二幅图可以看出
      2. 如果只是将最下角的4作为两位数10,加法表中存在3,而表头中
      3. 最大数为2,仍然不成立,所以这幅图四进制明显不对,由于表头
      4. 中最大值是2,所以表中大于二便要进位,即进制z=3,而n=4,
      5. z=n-1=X+1成立。
      6. 规范证明:已知表头中有n-1个个位数,设最大个位数X,由
      7. (1)的逆定理,L的值等于两位数个数,则正好存在一个两位数
      8. 时,L=1,并且两位数是这一行中最大的数,即等于L+X=1+X,
      9. X=n-2,L+X=n-1,因为X本身已经是最大的个位数,L=1又是除0
      10. 外最小的个位数,所以L+X=z,证得z=n-1

    这样我们的信息就完整了,解决题目
    z=n-1直接炸出进制,然后统计每一行两位数个数计算字母所代表的数字(** 并且这里两位数的高位上必定是1,->两个个位无论如何加不爆十位啊** )

    (3)判错

    怎么样构不成加法表?

    1. 对于一个数值,若某字母已经符合过,那一定是错误的

    2. 超级好懂的无脑法:再把整个矩阵遍历一遍,看看每一个位置上的字母对不对(注意字母顺序)

    3. 对于一个0,1以外的字母,只会在+0时出现,即一行中除表头只出现一次

    4. 然而这个特判写起来很繁琐,怎么快捷的判错??(这里其他题解大概讲了,但因为这种判错确实是较优的所以仍然选择这种,不再给出过多证明): 对比两种计算字母值方法的结果是否相同
      ##### 另一种计算字母值的方法:对于字母X,X=n-2-(X在两位数的个位上出现次数)(还是给一句吧不然难受,对于n-2上面已经出现过了,是最大的个位数,而X在两位数个位出现次数即二位数(z+X)的个数,将图左下角到右上角做对角线就能发现分布规律)

    (另外,一觉起来发现打表基本都被gank了,打开最优解发现了dfs 0ms的魔王,我这种蒟蒻可还是去睡觉吧) 如有不足请在评论区打脸,会及时补充更正题解

    code:

    1. // luogu-judger-enable-o2
    2. #include
    3. #define fu(i,q,w) for(register int i=q;i<=w;i++)
    4. #define fd(i,q,w) for(register int i=q;i>=w;i--)
    5. using namespace std;
    6. typedef long long ll;
    7. inline int read(){
    8. int ret=0,f=1;char c;
    9. while((c=getchar())<'0'||c>'9')if(c=='-')f=-1;
    10. while(c>='0'&&c<='9')ret=ret*10+(c-'0'),c=getchar();
    11. return ret*f;
    12. }
    13. char word[10];//记录字母
    14. char check[10];//检查重复
    15. string numx,numy;//储存输入数据、检查重复
    16. map<char,int> two;//一行中两位数个数
    17. map<char,int> tone;//存字母在两位数个位出现几次
    18. int n;
    19. void in(){
    20. n=read();
    21. cin>>numx;//"+"特判输入
    22. fu(i,1,n-1){cin>>numx,word[i]=numx[0];}// 第一行存表头的每个字母
    23. fu(i,1,n-1)//从第二行开始
    24. fu(j,1,n){cin>>numx;
    25. if(j!=1&&j!=2)//表头不算
    26. if(numx==numy){printf("ERROR!");exit(0);}//发现重复输入一定不对
    27. numy=numx; //前后比,不要全行比
    28. if(numx.size()==2){//统计两位数个数
    29. two[word[i]]++;tone[numx[1]]++;
    30. }
    31. }
    32. }
    33. void solve(){
    34. fu(i,1,n-1)
    35. if(two[word[i]]!=n-2-tone[word[i]]){printf("ERROR!");exit(0);}
    36. //比较两种算法的结果是否相同
    37. fu(i,1,n-1)
    38. cout<'='<' ';
    39. printf("\n");
    40. printf("%d",n-1);
    41. }
    42. int main(){
    43. in();
    44. solve();
    45. return 0;
    46. }

  • 相关阅读:
    mac建议装双系统吗,详细分析苹果电脑双系统的利弊
    【分布式系统】分布式选举之Bully算法
    A48基于NRF24L01的无线心率血氧体温检测
    Ubuntu 系统安装 VS Code 并配置 C++ 环境
    java.lang.OutOfMemoryError- unable to create new native thread 问题排查
    Hadoop3教程(二十二):Yarn的基础架构与工作流程
    STM32F103 UART4串口使用DMA接收不定长数据和DMA中断发送
    回文串 rust解法
    k8s--RC
    程序员怎样才能写出一篇好的博客或者技术文章?
  • 原文地址:https://blog.csdn.net/You_are_hanson/article/details/125891816