[NOIP1998 提高组] 进制位

[NOIP1998 提高组] 进制位

题意:

给定一个字母表 \(S\),第一行或第一列代表了所有的字符,每个字符对应一个数,且 \(s[i][1]=s[1][i]\)

其中 \(S[i][j]\) 表示 \(S[1][i]+S[j][1]\)

请你求出每个字母对应的数字和进位制,如果不行输出 \(ERROR!\)

分析:

首先可以有两个结论:

1. 如果有 \(n\) 个字符,那么进位一定是 \(n\)

证明:

因为有 \(n\) 个不同的数,所以肯定是 \(n\) 进制往上。

如果进制为 \(n+1\) 进制,那么一定有一个数没有出现,假设为 \(k\)

  1. \(k=0\)\(k=1\),而 \(1+n=10\) ,矛盾。
  2. \(1 \geq k \geq n\) ,而 \(1+(k−1)=k\) ,矛盾。

因此得证

2. 如果 \(i\) 这一列/行有 \(cnt\) 个两位的字符,那么这个 \(s[i][1]\) 字符代代表的数的就是 \(cnt\)

证明:

因为这个数和其他所有数都进行了加的计算,感性理解一下就可以证明。

因此,我们就可以愉快的做题了。

\(map\) 保存每个字符对应的值,然后再扫一遍判断以下是否合法就行了。

字符处理有点恶心,我是把长度为 \(2\) 的字符拆成了两个字符,分开算。

代码:

// P1013 [NOIP1998 提高组] 进制位

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

int n;
char s[12][12][5];
int len[12][12];
int cnt[12];
map<char,int> mp;

int main(){
    cin>>n; int m=n-1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            char a[3]; scanf("%s",a); if(i==1&&j==1) continue;
            len[i][j]=strlen(a);
            if(len[i][j]==1) s[i][j][0]=a[0];
            else cnt[j]++,s[i][j][0]=a[0],s[i][j][1]=a[1];
        }
    }
    for(int i=2;i<=n;i++) mp[s[1][i][0]]=cnt[i];
    int flag=0;
    for(int i=2;i<=n;i++)
        for(int j=2;j<=n;j++){
            if(len[i][j]==1){
                int a=mp[s[1][i][0]],b=mp[s[1][j][0]],c=mp[s[i][j][0]];
                if(a+b!=c) flag=1; 
            }
            else{
                int a=mp[s[1][i][0]],b=mp[s[1][j][0]],c=mp[s[i][j][0]],d=mp[s[i][j][1]];
                if(a+b!=c*m+d) flag=1;
            }
        }
    //虽然好多特判都没判,但是数据实在太水了就过了
    if(flag==1) puts("ERROR!");
    else{
        for(int i=2;i<=n;i++) cout<<s[1][i][0]<<"="<<cnt[i]<<" ";
        puts(""); cout<<m<<endl;
    }
    system("pause");
    return 0;
}
posted @ 2021-10-13 15:24  Evitagen  阅读(97)  评论(0编辑  收藏  举报