1. 程式人生 > 其它 >【NOIP1998 提高】 進位制位

【NOIP1998 提高】 進位制位

題目

題目背景

著名科學家盧斯為了檢查學生對進位制的理解,他給出瞭如下的一張加法表,表中的字母代表數字。

題目描述

例如:

+    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=KL\)

\(E+E=KV\)

根據這些規則可推匯出:\(L=0\)

\(K=1\)\(V=2\)\(E=3\)

同時可以確定該表表示的是 \(4\) 進位制加法。

輸入格式

第一行一個整數 \(n(3 \le n \le 9)\)
以下 \(n\) 行,每行包括 \(n\) 個字串,每個字串間用空格隔開。
若記 \(s_{i,j}\)表示第 \(i\) 行第 \(j\) 個字串,資料保證 \(s_{1,1}=+\) , \(s_{i,1}=s_{1,i}\) , \(|s_{i,1}=1|\) , \(s_{i,1} \neq s_{j,1} (i \neq j)\)
保證至多有一組解。

輸出格式

第一行輸出各個字母表示什麼數,格式如:\(L=0\) \(K=1 ……\)


按給出的字母順序排序。不同字母必須代表不同數字。
第二行輸出加法運算是幾進位制的。
若不可能組成加法表,則應輸出\(ERROR!\)

輸入輸出樣例

輸入 #1

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

輸出 #1

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

思路

進位制數?

一個顯然的想法是,字母所代表的數字不重複,所以進位制數\(l\)一定大於等於字母個數\(n-1\),即\(l \geq n-1\)
那麼到底是幾進位制?可以考慮列舉,注意資料範圍\((3 \leq n \leq 9)\),如果可以列舉進位制數\(l\)

\(n-1 ~ 10\),判斷在每個進位制下的加法表是否合法,判斷加法表考慮一下暴力\(n^2\),複雜度完全可以接受。

每個字母所代表的?

我們先嚐試構建個特殊的加法表,設\(a_{i,j}\)所代表的數字為\(digit_{i,j}\),構造一個字母表\((digit_{i_1,1} \leq digit_{i_2,1}\),且\(i_1 \leq i_2\),\(l=10\)),看看能否找找其中的規律?

+ 0 1 2 3 4 5 6
0 0 1 2 3 4 5 6
1 1 2 3 4 5 6 7
2 2 3 4 5 6 7 8
3 3 4 5 6 7 8 9
4 4 5 6 7 8 9 10
5 5 6 7 8 9 10 11
6 6 7 8 9 10 11 12

觀察每個斜線上的數字,可以發現,在\(2 \leq i,j \leq n\)時,每個數字出現的次數\(cnt\)減去\(1\)就是當前數字。
我們得到了一個特殊的情況,那麼該種情況能否向外推?
試著去手玩一下,任意交換兩個數字,保證加法表成立,只需要將對應的行與列都交換。
例如將以下加法表 第一個 第二個數字交換

+ 0 1 2 3
0 0 1 2 3
1 1 2 3 4
2 2 3 4 5
3 3 4 5 6
| |
V V
+ 1 0 2 3
1 2 1 3 4 
0 1 0 2 3 
2 3 2 4 5
3 4 3 5 6

顯然成立
因為只是交換了數字的位置,是整行整列的交換,而沒有直接改變數字,所以每個數字出現的次數\(cnt\)不變。
(原諒我想不出更加嚴謹的證明,只能“顯然”一下了)
那麼擴充套件到字母,同理,可以得出每個字母代表的數字為其出現的次數\(cnt-1\)

實現

對於處理每個字母出現的次數,可以採用\(map\),可以轉為\(ASCII\)碼開陣列,這裡我採用\(map\)
先記錄次數,再按\(l\)列舉,判斷合法性即可

CODE

#include <bits/stdc++.h>
using namespace std;
string a[10][10];
map<char,int>cnt;
int main(){
	int n;
	int ans=0;
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++) cin>>a[i][j];
	for(int i=2;i<=n;i++)
		for(int j=2;j<=n;j++){
			if(a[i][j].size()==1)
			cnt[a[i][j][0]]++;
		}
	for(int l=n-1;l<=10;l++){
		bool f=1;//標記 方案是否合法
		for(int i=2;i<=n;i++){
			for(int j=2;j<=n;j++){
				int sum=0;
				for(int k=a[i][j].size()-1;k>=0;k--){
					sum+=(cnt[a[i][j][k]]-1)*pow(l,a[i][j].size()-k-1);//l進位制下的數字轉換為10進位制 方便比較
				}
				if(sum!=(cnt[a[i][1][0]]-1+cnt[a[1][j][0]]-1)){f=0; break;}//方案非法 可以列舉下一個進位制了
			}
			if(!f) break;
		}
		if(!f) continue;
		ans=l;
		break;
	}
	if(ans==0){
		puts("ERROR!");
	}else{
		for(int i=2;i<=n;i++) cout<<a[1][i][0]<<'='<<cnt[a[1][i][0]]-1<<' ';
		puts("");
		cout<<ans<<endl;
	}
}