蟲食算是一種用字母或者說空格來代替數字的算式,由做題者復原算式。現在,我們一般稱其為“數字謎”或者說“數字謎題”。它對於開發智力具有一定的作用。
基本介紹
- 中文名:蟲食算
- 屬於:算式
- 包含:用字母或者說空格來代替數字
- 對於:開發智力具有一定的作用
出處,問題描述,輸入檔案,輸出檔案,樣例輸入,樣例輸出,數據規模,解題報告,代碼清單,
出處
NOIP2004提高組複賽最後一題:蟲食算。
蟲食算
(alpha.pas/dpr/c/cpp)
問題描述
所謂蟲食算,就是原先的算式中有一部分被蟲子啃掉了,需要我們根據剩下的數字來判定被啃掉的字母。來看一個簡單的例子:
43#98650#45
+ 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個不同的字母分別代表的數字,使得該加法算式成立。輸入數據保證有且僅有一組解,
輸入檔案
輸入檔案包含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。
解題報告
計算機在某一位搜尋出了一個算式1 + 1 = 3,並且繼續搜尋。
明顯,人眼很容易就看出這是不合法的,但計算機不會。於是,我們想到了第一個剪枝:每次搜尋的時候,從最後向前判斷是否有不合法的式子。
這一個剪枝非常簡單,但是效果卻非常的好。因為它剪去了很多不必要的搜尋。為了配合這一種剪枝更好的實行,搜尋順序的改變也成為大大提高程式效率的關鍵:從右往左,按照字母出現順序搜尋,有很大程度上提高了先剪掉廢枝的情況,使程式的效率得到大大的提高。
有了以上兩個剪枝,程式就已經可以通過大部分測試點了。但是有沒有更多的剪枝呢?答案是肯定的。
根據前面的剪枝,我們可以找到類似的幾個剪枝:
對於a + b = c的形式,假如:
A***?***
+ B*?**?**
C***???*
其中*代表已知,?代表未知。那么,A + B與C的情況並不能直接確定。但是,假如(A + B) % N與(A + B + 1) % N都不等於C的話,那么這個等式一定是不合法的。因為它只有進位和不進位的兩種情況。
同樣,我們在一個數組里記錄了Used表示一個數字有沒有用過,那么,對於某一位A + B = C的等式,如果已經得到了兩個數,另一個數還待搜尋的時候,我們還可以根據這個加入一個剪枝:
例如A + ? = C的形式,
考慮不進位的情況,則?處為P1 = (C - A + N) % N
假如考慮進位的情況,則?處為P2 = (C - A - 1 + N) % N
假如P1、P2均被使用過,那么這個搜尋一定是無效的,可以剪去。
有了以上的剪枝,就可以很輕鬆地通過所有的測試數據了。當然,還有很多值得思考的剪枝以及其他的思路,例如枚舉進位、解方程(但是可能需要枚舉)等,在這裡就不詳細討論了。
代碼清單
C++語言實現
#include <fstream>#include <string>using namespace std;ifstream fin;ofstream fout( "alpha.out" );bool finish, hash[256], used[27];int n, stk[27];string a, b, c;string word;void init(){ fin >> n >> a >> b >> c; finish = false;}void outsol(){ int i, ans[27]; for ( i = 0; i < n; i++ ) ans[word - 65] = stk; fout << ans[0]; for ( i = 1; i < n; i++ ) fout << " " << ans; fout << endl; finish = true;}void addup( char ch ){ if ( !hash[ch] ) { hash[ch] = true; word = word + ch; }}string change( string str, char x, char y ){ for ( int i = 0; i < n; i++ ) if ( str == x ) str = y; return(str);}void pre_doing(){ word = ""; memset( hash, 0, sizeof(hash) ); for ( int i = n - 1; i >= 0; i-- ) { addup( a ); addup( b ); addup( c ); } memset( used, 0, sizeof(used) );}bool bad(){ int p, g = 0; for ( int i = n - 1; i >= 0; i-- ) { if ( a >= n || b >= n || c >= n ) return(false); p = a + b + g; if ( p % n != c ) return(true); g = p / n; p %= n; } return(false);}bool modcheck(){ int i, p, p1, p2, g = 0;/* a + b = c, all know */ for ( i = n - 1; i >= 0; i-- ) { if ( a >= n || b >= n || c >= n ) continue; p = (a + b) % n; if ( !(p == c || (p + 1) % n == c) ) return(true); }/* a + ? = c */ for ( i = n - 1; i >= 0; i-- ) { if ( !(a < n && c < n && b >= n) ) continue; p1 = (c - a + n) % n; p2 = (p1 - 1) % n; if ( used[p1] && used[p2] ) return(true); }/* ? + b = c */ for ( i = n - 1; i >= 0; i-- ) { if ( !(a >= n && c < n && b < n) ) continue; p1 = (c - b + n) % n; p2 = (p1 - 1) % n; if ( used[p1] && used[p2] ) return(true); }/* a + b = ? */ for ( i = n - 1; i >= 0; i-- ) { if ( !(a < n && b < n && c >= n) ) continue; p1 = (a + b) % n; p2 = (p1 + 1) % n; if ( used[p1] && used[p2] ) return(true); } return(false);}void dfs( int l ){ int i; string A, B, C; if ( finish ) return; if ( bad() ) return; if ( modcheck() ) return; if ( l == n ) { outsol(); return; } for ( i = n - 1; i >= 0; i-- ) if ( !used ) { used = true; A = a; B = b; C = c; a = change( A, word[l], i ); b = change( B, word[l], i ); c = change( C, word[l], i ); stk[l] = i; dfs( l + 1 ); used = false; a = A; b = B; c = C; }}int main(){ init(); pre_doing(); dfs( 0 ); return(0);}
pascal實現
var
a:Array[1..3,1..26]of char;
b:array['A'..'Z']of longint;
c:array[0..25]of boolean;
i,j,n:longint;
function check(x,m:longint):boolean;
var
yy:boolean;
i,j:longint;
begin
if ((b[a[1,x]]+b[a[2,x]]+m)mod n<>b[a[3,x]]) then exit(false);
for i:=1 to x-1 do
if (b[a[1,i]]<>-1)and(b[a[2,i]]<>-1)and(b[a[3,i]]<>-1)then
begin
yy:=true;
for j:=0 to 1 do
if (b[a[1,i]]+b[a[2,i]]+j)mod n=b[a[3,i]] then yy:=false;
if yy then exit(false);
end;
check:=true;
end;
procedure dfs(x,m:longint);
var i,j,d,p,mm:longint;
aa,bb,cc:boolean;
begin
if x=0 then begin
if m=0 then
begin
for i:=1 to n do write(b[chr(i+64)],' ');
close(input);close(output);
halt;
end;
exit;
end;
aa:=false;
bb:=false;
cc:=false;
if b[a[1,x]]<>-1 then aa:=true;
if b[a[2,x]]<>-1 then bb:=true;
if b[a[3,x]]<>-1 then cc:=true;
if (aa and bb and cc) then
begin
if check(x,m) then dfs(x-1,(m+b[a[1,x]]+b[a[2,x]])div n);
end
else if aa and bb then
begin
d:=(b[a[1,x]]+b[a[2,x]]+m)mod n;
if c[d] then
begin
b[a[3,x]]:=d;
c[d]:=false;
mm:=(b[a[1,x]]+b[a[2,x]]+m)div n;
if check(x,m) then dfs(x-1,mm);
c[d]:=true;
b[a[3,x]]:=-1;
end;
end
else if aa and cc then
begin
d:=(b[a[3,x]]-m-b[a[1,x]]+n)mod n;
if c[d] then
begin
b[a[2,x]]:=d;
c[d]:=false;
mm:=(d+b[a[1,x]]+m)div n;
if check(x,m) then dfs(x-1,mm);
m:=mm;
c[d]:=true;
b[a[2,x]]:=-1;
end;
end
else if bb and cc then
begin
d:=(b[a[3,x]]-m-b[a[2,x]]+n)mod n;
if c[d] then
begin
b[a[1,x]]:=d;
c[d]:=false;
mm:=(d+b[a[2,x]]+m)div n;
if check(x,m) then dfs(x-1,mm);
m:=mm;
c[d]:=true;
b[a[1,x]]:=-1;
end;
end
else if (not aa)and(not bb)and(not cc) then
begin
if a[1,x]<>a[2,x] then
begin
for i:=n-1 downto 0 do
for j:=n-1 downto 0 do
if (c[i] and c[j])and(i<>j) then
begin
c[i]:=false;
c[j]:=false;
b[a[1,x]]:=j;
b[a[2,x]]:=i;
dfs(x,m);
c[i]:=true;
c[j]:=true;
b[a[1,x]]:=-1;
b[a[2,x]]:=-1;
end;
end
else
for i:=n-1 downto 0 do
if c[i] then
begin
b[a[1,x]]:=i;
c[i]:=false;
dfs(x,m);
b[a[p,x]]:=-1;
c[i]:=true;
end;
end
else begin
for i:=n-1 downto 0 do
if c[i] then
begin
if not aa then p:=1
else if not bb then p:=2
else if not cc then p:=3;
b[a[p,x]]:=i;
c[i]:=false;
dfs(x,m);
b[a[p,x]]:=-1;
c[i]:=true;
end;
end;
end;
begin
readln(n);
for i:=1 to 3 do
begin
for j:=1 to n do
read(a[i,j]);
readln;
end;
fillchar(b,sizeof(b),255);
fillchar(c,sizeof(c),true);
dfs(n,0);
end.
總結
搜尋題的框架往往不難找到,關鍵就是在搜尋的最佳化上,本文的主要篇幅也就是討論了幾種有效的最佳化。搜尋問題的最佳化更多的需要選手的經驗和思考、分析問題的能力,所以搜尋剪枝也是競賽中經久不衰的經典問題。