プログラム例 /* ACM-ICPC2009 国内予選 Problem C */ // // filename = pc1.c // コンパイル

Similar documents
文字数は1~6なので 同じ本数の枝を持つパスで生成される呪文の長さは最大で6 倍の差がある 例えば 上図のようなケースを考える 1サイクル終了した時点では スター節点のところに最強呪文として aaaaaac が求まる しかしながら サイクルを繰り返していくと やがてスター節点のところに aaaaaa

[Problem F] 古い記憶 良さそうな方法は思いつかなかった アイディア募集中!!! かなり強引に解いている 各判定データに対して 30 秒程度かかる 元の文章と改ざん文章の関係を考える ウィルス感染の可能性は高々 2 回であり 各々の感染では 1 文字削除 1 文字追加 1 文字変更が行われ

ープのロープ長以下であれば実現可能である ケース 3: 3 本のロープの杭の位置を点 P 1 = (x 1, y 1, 0), 点 P 2 = (x 2, y 2, 0), 点 P 3 = (x 3, y 3, 0) とする 点 P 1 = (x 1, y 1, 0), 点 P 2 = (x 2,

Taro-再帰関数Ⅲ(公開版).jtd

Taro-再帰関数Ⅱ(公開版).jtd

Microsoft Word - no103.docx

Taro-スタック(公開版).jtd

次に示す数値の並びを昇順にソートするものとする このソートでは配列の末尾側から操作を行っていく まず 末尾の数値 9 と 8 に着目する 昇順にソートするので この値を交換すると以下の数値の並びになる 次に末尾側から 2 番目と 3 番目の 1

[Problem D] ぐらぐら 一般に n 個の物体があり i 番目の物体の重心の x 座標を x i, 重さを w i とすると 全体の n 重心の x 座標と重さ w は x = ( x w ) / w, w = w となる i= 1 i i n i= 1 i 良さそうな方法は思いつかなかった

プログラミング基礎

gengo1-8

PowerPoint プレゼンテーション

< F2D837C E95CF CF68A4A94C5816A2E6A>

コマンドラインから受け取った文字列の大文字と小文字を変換するプログラムを作成せよ 入力は 1 バイトの表示文字とし アルファベット文字以外は変換しない 1. #include <stdio.h> 2. #include <ctype.h> /*troupper,islower,isupper,tol

Taro-プログラミングの基礎Ⅱ(公

PowerPoint プレゼンテーション

Microsoft Word - COMP-MATH-2016-FULLTEXT.doc

本サンプル問題の著作権は日本商工会議所に帰属します また 本サンプル問題の無断転載 無断営利利用を厳禁します 本サンプル問題の内容や解答等に関するお問 い合わせは 受け付けておりませんので ご了承ください 日商プログラミング検定 STANDARD(C 言語 ) サンプル問題 知識科目 第 1 問 (

C プログラミング演習 1( 再 ) 2 講義では C プログラミングの基本を学び 演習では やや実践的なプログラミングを通して学ぶ

PowerPoint プレゼンテーション

Prog1_10th

Taro-リストⅠ(公開版).jtd

Microsoft Word - no202.docx

PowerPoint プレゼンテーション

program7app.ppt

C 言語第 3 回 2 a と b? 関係演算子 a と b の関係 関係演算子 等しい a==b 等しくない a!=b より大きい a>b 以上 a>=b より小さい a<b 以下 a<=b 状態 真偽 値 条件が満たされた場合 TRUE( 真 ) 1(0 以外 ) 条件が満たされなかった場合 F

Microsoft PowerPoint - lec4.ppt

Taro-リストⅢ(公開版).jtd

Microsoft PowerPoint - lec10.ppt

Microsoft PowerPoint - C言語の復習(配布用).ppt [互換モード]

Taro-最大値探索法の開発(公開版

ポインタ変数

データ構造

Microsoft Word - no15.docx

PowerPoint Presentation

kiso2-09.key

プログラミング方法論 II 第 14,15 回 ( 担当 : 鈴木伸夫 ) 問題 17. x 座標と y 座標をメンバに持つ構造体 Point を作成せよ 但し座標 は double 型とする typedef struct{ (a) x; (b) y; } Point; 問題 18. 問題 17 の

[1] #include<stdio.h> main() { printf("hello, world."); return 0; } (G1) int long int float ± ±

Microsoft PowerPoint - 計算機言語 第7回.ppt

第9回 配列(array)型の変数

4 分岐処理と繰返し処理 ( 教科書 P.32) プログラムの基本的処理は三つある. (1) 順次処理 : 上から下に順番に処理する ぶんきそろ (2) 分岐処理 : 条件が揃えば, 処理する はんぷく (3) 反復処理 : 条件が揃うまで処理を繰り返す 全てのプログラムは (1) から (3) の

データ構造

Microsoft Word - no12.doc

講習No.12

Microsoft Word - no11.docx

本サンプル問題の著作権は日本商工会議所に帰属します また 本サンプル問題の無断転載 無断営利利用を厳禁します 本サンプル問題の内容や解答等に関するお問 い合わせは 受け付けておりませんので ご了承ください 日商プログラミング検定 STANDARD(Java) サンプル問題 知識科目 第 1 問 (

ポインタ変数

<4D F736F F D20438CBE8CEA8D758DC F0939A82C282AB2E646F63>

Microsoft PowerPoint - program.ppt [互換モード]

講習No.9

Java講座

PowerPoint Presentation

gengo1-12

Microsoft PowerPoint - 5Chap15.ppt

PowerPoint プレゼンテーション

演習課題No12

今までの復習 プログラムで最低限必要なもの 入力 ( キーボードから ファイルから ) 出力 ( 画面へ ファイルへ ) 条件分岐 : 条件の成立 不成立により 異なる動作をする 繰り返し : 一定の回数の繰返し 条件成立の間の繰返し 関数の定義 関数の呼び出し C ではそれ以外に ポインタ データ

gengo1-10

人工知能入門

gengo1-12

データ構造

gengo1-6

第1回 プログラミング演習3 センサーアプリケーション

kiso2-03.key

Microsoft PowerPoint - CproNt02.ppt [互換モード]

memo

PowerPoint プレゼンテーション

Microsoft Word - 3new.doc

Microsoft PowerPoint - 4.pptx

C 言語講座 Vol 年 5 月 29 日 CISC

プログラミング実習I

PowerPoint Presentation

Microsoft PowerPoint - ca ppt [互換モード]

gengo1-12

PowerPoint プレゼンテーション

関数の呼び出し ( 選択ソート ) 選択ソートのプログラム (findminvalue, findandreplace ができているとする ) #include <stdiu.h> #define InFile "data.txt" #define OutFile "surted.txt" #def

Microsoft Word - Training10_プリプロセッサ.docx

ポインタ変数

memo

文字列 2 前回の授業ではコンピュータ内部での文字の取り扱い 文字型の変数 文字型変数への代入方法などを学習した 今回は 前回に引き続き 文字処理を学習する 内容は 標準入出力 ( キーボード ディスプレイ ) での文字処理 文字のファイル処理 文字を取り扱うライブラリ関数である 標準入出力 Lin

printf("5つの整数を入力して下さい \n"); /* データ入力 */ for( /*** 02 ***/ ){ printf("%dつ目の入力 :",i+1); scanf("%d", /*** 03 ***/ ); sum=dat[0]; /* 合計値の初期設定 */ n_max= 0


JavaプログラミングⅠ

Taro-ファイル処理(公開版).jtd

Microsoft Word - report#8.docx

char int float double の変数型はそれぞれ 文字あるいは小さな整数 整数 実数 より精度の高い ( 数値のより大きい より小さい ) 実数 を扱う時に用いる 備考 : 基本型の説明に示した 浮動小数点 とは数値を指数表現で表す方法である 例えば は指数表現で 3 書く

Microsoft PowerPoint - C4(反復for).ppt

(1) プログラムの開始場所はいつでも main( ) メソッドから始まる 順番に実行され add( a,b) が実行される これは メソッドを呼び出す ともいう (2)add( ) メソッドに実行が移る この際 add( ) メソッド呼び出し時の a と b の値がそれぞれ add( ) メソッド

基礎計算機演習 実習課題No6

ポインタ変数

始めに, 最下位共通先祖を求めるための関数 LcaDFS( int v ) の処理を記述する. この関数は値を返さない再帰的な void 関数で, 点 v を根とする木 T の部分木を深さ優先探索する. 整数の引数 v は, 木 T の点を示す点番号で, 配列 NodeSpace[ ] へのカーソル

Microsoft Word - Cプログラミング演習(11)

XMPによる並列化実装2

プログラミング及び演習 第1回 講義概容・実行制御

フローチャートの書き方

スライド 1

PowerPoint プレゼンテーション

Taro-2分探索木Ⅱ(公開版).jtd

関数の呼び出し ( 選択ソート ) 選択ソートのプログラム (findminvalue, findandreplace ができているとする ) #include <stdio.h> #define InFile "data.txt" #define OutFile "sorted.txt" #def

7 ポインタ (P.61) ポインタを使うと, メモリ上のデータを直接操作することができる. 例えばデータの変更 やコピーなどが簡単にできる. また処理が高速になる. 7.1 ポインタの概念 変数を次のように宣言すると, int num; メモリにその領域が確保される. 仮にその開始のアドレスを 1

Transcription:

[Problem C] 覆面計算 与えられた覆面計算に対して 等式を満たすような数字の割り当てが何通りあるか を求める問題であるので 各文字に対する数字の割り当てを順番に生成していき その割り当てが等式を満たすかどうかチェックすればよい 異なる文字には異なる数値 (0~9) が割り当てられるので 最大で 10! 約 360 万通りの組み合わせをチェックする必要がある (1 桁の場合を除いて最上位の桁は 0 で無いのでチェックすべき組み合わせの数はもう少し少なくなる ) あまり工夫しなくても何とかなりそう!!? 例えば ACM + IBM = ICPC は 100 A + 10 C + M + 100 I + 10 B + M = 1000 I + 100 C + 10 P + C を意味する これを整理すると 100 A - 91 C + 2 M -900 I + 10 B -10 P = 0 となる この係数を事前に求めておき A,C,M,I,B,P の全てに数字を割り当てた時にこの式に代入してチェックする 各文字に対する全ての可能な割当を組織的に生成し 等式が成立する回数を数える 全ての可能な割当を組織的に生成する方法 ( 順列の生成 ) については 以下のプログラム例の再帰的関数 perm を参照 以下のプログラム例を上記の例に適用した場合 main 関数から perm(0) を呼び出す直前には nc = 6 (6 種類の文字が現れている ) c = {'A', 'C', 'M', 'I', 'B', 'P', 割り当て可能な数値のリスト nu = {0, 1, 2, 3, 4, 5, 6, 7, 8. 9 値 0 が割当不可であることを示すリスト zskip = {1, 0,0,1,0,0, (A, I は2 桁以上の数値の最上位桁になるので 値 0 を割り当てることはできない ) 等式が成立する割り当ての個数 count = 0 perm(0) が呼び出されると その内部では次のように動作する 呼び出された時点で nu[0]~nu[9] に未割当の数値が格納されている for 文の最初の繰り返し (i=0) では 値 0 (= val = nu[0]) が割り当てる数値として選ばれるが この文字に値 0 は割り当てられない (zl = 1) ので何もせずに次の繰り返しに入る for 文の2 回目の繰り返し (i=1) では 値 1 (= val = nu[1]) が選ばれその値を最初の文字 (A) の値として割り当てる そして nu[1] = nu[9] により nu = {0, 9, 2, 3, 4, 5, 6, 7, 8, 9 としてから perm(1) を呼び出している perm(1) では nu[0]~nu[8] 即ち {0, 9, 2, 3, 4, 5, 6, 7, 8 が呼び出し時点での未割当数値となっている perm(0) では perm(1) から戻ってきた時点で nu[1] = val (= 1) により値 1 を未割当数値に戻して for 文の次の繰り返し行っている 以下のプログラム例を Core i7 1.8GHz 上で4 種類の判定データに適用すると 各々 2 3 秒程度で結果が得られる 1

プログラム例 /* ACM-ICPC2009 国内予選 Problem C */ // http://www.waseda.jp/assoc-icpc2009/preliminary/contest/all_ja.html#section_c // filename = pc1.c // コンパイル : cc -O2 pc1.c // 実行方法 :./a.out < C0 > C0.result 等 // 確認方法 : diff C0.ans C0.result 等 // アルゴリズム : 覆面算に現れるアルファベットに対して互いに異なる数値を割当 // その割当が覆面算の等式を満たすかどうか判定する #include <stdio.h> #include <string.h> #define MAXN 12 #define MAXLEN 8 // 文字列の個数 N の最大値 // 文字列の長さの最大値 int n; // N, 3 <= n <= 12 char strs[maxn][maxlen+1]; // STRING_i を strs[i-1][ ] に格納 int len[maxn]; // 各 STRING の長さ char c[10]; // STRING に表れる文字. 最大でも 10 個 int v[10]; // 各文字に割当てた値 int a[10]; // 各文字の係数 int zskip[10]; // 1 -> 値 0 は割当不可 int nc; // 使われている文字数 int weight[] = {1,10,100,1000,10000,100000,1000000,10000000; // 桁位置の重み int count; // 等式を満たす割当の個数 int nu[10]; // 未使用の数値リスト // 全ての文字に対して数値が割当済みの時は等式が成立するかをチェック // 数値未割当の文字が残っている場合は 未使用の数値を順番に割当てる void perm(int level) // level = 数値割当済みの文字数 { int i, val, zl; if(level == nc){ // 全ての文字に対して数値割当済みなら val = 0; // その割当に対する条件式の値を計算 for(i=0; i<nc; i++) val += v[i]*a[i]; if(val == 0) count++; // 値が 0 なら等式成立 return; zl = zskip[level]; // zl = 1 なら 0 は割当不可 // 現時点で未使用の数値は nu[0] nu[9-level] に格納されている for(i=0; i<10-level; i++){ // 未使用の数値を順に割当てていく val = nu[i]; // val は未使用の数値 if(val==0 && zl) continue; // 0 が割当不可なら次の未使用数値を割当てる v[level] = val; // val を割当てる nu[i] = nu[9-level]; // val は使用済みとする perm(level+1); // 再帰的に次の文字に数値を割当てていく nu[i] = val; // 次の割当を試す前に val を未使用に戻す int main() 2

{ int i,j,k; while(1){ scanf("%d", &n); // N を入力 if(n==0) break; // N=0 なら終了 for(i=0; i<n; i++){ // n 個の文字列を読み込む scanf("%s", &strs[i][0]); // 文字列を読み込み len[i] = strlen(&strs[i][0]); // その長さをセット nc = 0; for(i=0; i<10; i++) a[i] = zskip[i] = 0; for(i=0; i<n; i++){ char *s; s = &strs[i][0]; // s は i 番目の文字列 for(j=0; j<len[i];j++){ char d; int w; d = s[j]; // d は s の j 番目の文字 for(k=0; k<nc; k++){ // d が登録済みかどうかをチェック if(d == c[k]) break; // d は登録済み if(k==nc) c[nc++] = d; // k==nc のとき d は新しい文字なので新規登録 // k は現在注目している文字 (d) が登録されている位置 if(j==0 && len[i]>1){ // 長さ 2 以上の文字列の最上位桁なら zskip[k] = 1; // 値 0 は割当不可 w = weight[len[i]-j-1]; // 文字 d が表れる桁位置からその重みを求める if(i==n-1) w *= -1; // 右辺に表れる文字の係数は負 a[k] += w; count = 0; for(i=0; i<10; i++) nu[i] = i; // 未割当数値のリストを初期化 ( 全てみ割当 ) perm(0); // 全ての可能な割当を組織的に生成して条件式が成立する回数を数える printf("%d\n",count); // 求まった結果を出力 より効率を向上させるには 下位の桁に現れる文字から数値を割当て行き 下位 i 桁の現れる文字に数値が割り当てられた時点で 下位 i 桁の等式が成立するかどうかをチェックし 不成立なら未割当の文字に対する数値割り当てをスキップする方法が考えられる 例えば ACM + IBM = ICPC で M, C, B, P, A, I の順に数値割り当てを行うものとする この時 M と C に数値を割り当てた時点で下位 1 桁に現れる全ての文字に数値が割り当てられているので 下位 1 桁に関する等式 (2 M - C) % 10 =0 をチェックし これが不成立なら残りの文字に対する数値割り当てをスキップする ( 枝狩 ) 成立する場合は 残りの文字に対する数値割当を継続する M, C, B, P まで数値 3

割当を行うと下位 2 桁に現れる全ての文字に数値が割り当てられているので 下位 2 桁に関する等式 (9 C + 2 M + 10 B - 10 P) % 100 = 0 をチェックし これが不成立なら残りの文字に対する数値割り当てをスキップする 成立する場合は 残りの文字に対する数値割当を継続する M, C, B, P, A, I まで数値割り当てを行うと 全ての文字の数値割当が決まるので 全体の等式 100 A - 91 C + 2 M -900 I + 10 B -10 P = 0 をチェックし この割り当てが等式を満たすかどうかを判定する この考え方に基づくプログラム例を以下に示す Core i7 1.8GHz 上で4 種類の判定データに適用すると 各々 0.5 秒程度で結果が得られる 国内予選では プログラムの実行速度は問われないので 2 3 秒程度で結果が得られる前述の簡単なプログラムで OK. /* ACM-ICPC2009 国内予選 Problem C */ // http://www.waseda.jp/assoc-icpc2009/preliminary/contest/all_ja.html#section_c // filename = pc2.c // コンパイル : cc -O2 pc2.c // 実行方法 :./a.out < C0 > C0.result 等 // 確認方法 : diff C0.ans C0.result 等 // アルゴリズム : 覆面算に現れる文字に対して下位の桁に表れるものから順に // 互いに異なる数値を割当る // 最下位桁に表れる文字への数値割当がすんだ時点で // 覆面算の最下位桁の等式が成立するかをチェックし // 不成立なら上位桁に対する割当をスキップする // 最下位桁の等式が成立するなら 次の文字に対する割当を継続し // 下位 2 桁に表れる文字への数値割当がすんだ時点で // 覆面算の下位 2 桁の等式が成立するかをチェックする // このような処理を最上位桁まで繰り返す #include <stdio.h> #include <string.h> #define MAXN 12 #define MAXLEN 8 // N の最大値 // 文字列の長さの最大値 int n; // N, 3 <= n <= 12 char strs[maxn][maxlen+1]; // STRING_i を strs[i-1][ ] に格納 int len[maxn]; // 各 STRING の長さ char c[10]; // STRING に表れる文字 int v[10]; // 各文字に割当てた値 int a[10][10]; // 各文字の係数. a[i][ ] は下から i 桁目までの等式の係数 // ただし a[0][ ] は全体の等式 int ce[10]; // ce[i] = i 文字目まで割当てた時点でチェックすべき等式番号 int md[10]; // 各文字が表れる最下位の桁位置 int maxlen = 0; int nc; // 使われている文字数 int zskip[10]; // 1 -> 値 0 は割当不可 4

// 桁位置の重み int weight[] = {1,10,100,1000,10000,100000,1000000,10000000; int count; // 等式を満たす割当の個数 int nu[10]; // 未使用の数値リスト // この時点でチェック可能な等式があればチェックする // 数値を未割当の文字が残っている場合は 未使用の数値を順番に割当てる void perm(int level) // level = 数値割当済みの文字数 { int i,j; int val; int zl; int *ap,*vp; if(level == nc){ // 全ての文字に対して数値割当ずみ ap = &a[0][0]; // check する条件式の係数の配列 val = 0; for(i=0; i<nc; i++) val += v[i] * (*ap++); if(val == 0) count++; return; if(ce[level] > 0){ // この時点でチェック可能な条件式がある ap = &a[ce[level]][0]; // その条件式の係数の配列 val = 0; for(i=0; i<level; i++) val += v[i] * (*ap++); val = val % weight[md[level]-1]; // 下位 md[level] 桁で判定 if(val!= 0) return; // 条件不成立なら残っている文字への数値割当不要 zl = zskip[level]; // zl = 1 なら 0 は割当不可 for(i=0; i<10-level; i++){ // 未使用の数値を順に割当てていく val = nu[i]; // val は未使用の数値 if(val==0 && zl) continue; // 0 が割当不可なら次へ v[level] = val; // val を割当てる nu[i] = nu[9-level]; // val は使用済みとする perm(level+1); // 再帰的に次の文字に数値を割当てていく nu[i] = val; // val を未使用に戻す int main() { int i,j,k; while(1){ scanf("%d", &n); // N を入力 if(n==0) break; // N=0 なら終了 for(i=0; i<n; i++){ // n 個の文字列を読み込む scanf("%s", &strs[i][0]); // 文字列を読み込み len[i] = strlen(&strs[i][0]); // その長さをセット if(len[i] > maxlen) maxlen = len[i]; // 文字列の最大長を更新 nc = 0; 5

for(i=0; i<10; i++){ c[i] = md[i] = -1; zskip[i] = 0; for(i=0; i<10; i++) for(j=0;j<10; j++) a[i][j] = 0; for(i=1; i<=8; i++) for(j=0; j<n; j++){ char *s; int d,w,lj; lj = len[j]; s = &strs[j][0]; if(i <= lj){ d = s[lj-i]; for(k=0; k<nc; k++){ if(c[k] == d) break; if(k==nc){ md[k] = i; c[nc++] = d; if(i == lj && lj > 1) zskip[k] = 1; w = weight[i-1]; if(j == n-1) w *= -1; a[i][k] += w; // 下位 i 桁の文字への数値割当決定時の条件式 a[0][k] += w; // 全ての文字への数値割当決定時の条件式 // 下位 i 桁以下の文字への数値割当決定時の条件式を求める for(i=2; i<= maxlen; i++) for(j=0; j<nc; j++) a[i][j] += a[i-1][j]; // i 個の文字への数値割当決定時にチェックすべき条件式の番号を求める for(i=1; i<nc; i++) if(md[i-1]!= md[i]) ce[i] = md[i]-1; else ce[i] = 0; ce[0] = 0; count = 0; for(i=0; i<10; i++) nu[i] = i; perm(0); printf("%d\n",count); 6