カメヲラボ

主にプログラミングとお勉強全般について書いてます

Maya Calendar

http://acm.pku.edu.cn/JudgeOnline/problem?id=1008
表記方法の異なる2つのカレンダーで、データの変換を行う問題。

  • 最短コード295B

colunさんが超絶テクニックで非常に短いコードを書くことに成功した。
http://d.hatena.ne.jp/colun/20061108#p1

この問題の肝は、colunさんの解説でもあるように入力月の判定方法。文字列処理が絡んだコードを短縮しようとすると、文字コードまで考慮したコードを書かなければならない事が多い。解説を見て、大した事ないと思った人は間違い。こういうコードは見た目以上に多大な労力が必要になります。リテラルのサーチプログラム書いたらすぐ出来るじゃん、と考えるだけで実行しない人が大半です。実行してこそ、ショートコーダー。

さて、この非常に短いコードを眺めた時、非常に気になる部分があります。
月名の文字列と、逆写像配列。文字列の方は、Cのコードだと圧縮が難しいのでしかたなしとしても逆写像配列の部分は中身が小さな整数なので、圧縮可能です。整数値が0〜18という非常に小さな値ですから、これらはすべて文字コードと見なしてしまいましょう。さらに、

d+=r[(k^'Spa5')%21]*20+y*365;

の部分で20倍しているので、ここも無駄のように感じます。しかし配列データをそのまま20倍してしまうと、最大値18*20=360で表示可能な文字コード127を越えてしまいます。127を越えない値で考えると、4倍が好ましい。最初から4倍した値を埋め込んでおけば、*20の部分が*5になり1バイトお得だからです。5倍はダメなのか?ということになりますが、5倍だとダメです。配列内の2という値を5倍すると、これは改行コードを意味し、表記には'\n'と2バイト必要だから1バイトの損です。

というわけで、{13,6,11,7,10,2,1,15,16,3,18,4,0,12,9,8,5,17,14}を4倍します。0は4倍しても0で問題ないといえばないのですが、大半のエディタはヌル文字が嫌いのようなので0以外の値にしておいたほうが無難でしょう。あとはこれらを文字列として出力し、コードの中に埋め込んでしまえば完成です。

y;*s[]={"imix","ik","akbal","kan","chicchan","cimi",
"manik","lamat","muluk","ok","chuen","eb","ben","ix",
"mem","cib","caban","eznab","canac","ahau"};
main(d,k){
  for(puts(gets(k));~scanf("%d.%s%d",&d,&k,&y);
    printf("%d %s %d\n",d%13+1,s[d%20],d/260))
    d+="*******************"[(k^'Spa5')%21]*5+y*365;
}

 *の所は制御文字も含まれているので、各自で文字列を生成して埋め込んでください。