ラベル AtCoder の投稿を表示しています。 すべての投稿を表示
ラベル AtCoder の投稿を表示しています。 すべての投稿を表示
2013/02/10

[競技プログラミング][PHP][AtCoder]アキレスと亀

B - アキレスと亀

時間制限 : 2sec / スタック制限 : 16MB / メモリ制限 : 64MB

問題文

高橋君は、カメが大好きです。高橋君は L メートル先にカメを見つけたので、
このカメを追いかけて、捕まえようと思いました。
ですが、カメは高橋君が苦手です。
カメは、高橋君から追いかけられ始めた瞬間、高橋君と反対の方向に逃げていきます。
高橋君の追いかける速度は秒間 va メートルで、カメの逃げる早さは秒間 vb メートルです。

ここで高橋君はふと疑問に思いました。
高橋君が、今カメのいる場所までたどり着いた時、カメはさらに少し先に進んでいます。
そのカメがいる場所まで高橋君がたどり着くと、カメはその少し先にまた進んでいます。
これでは何度繰り返しても永遠にカメに追いつかないのではないでしょうか。

高橋君は、この事実を調査するため、
この動作を N 回繰り返した時に、カメと高橋君の距離がどれだけ縮まっているかを調べたいです。
高橋君とカメの距離を出力するプログラムを作成してください。
なお、高橋君はカメがいるところまでまっすぐ最短距離を進みます。
また、動作を開始した時点でカメがいたところまで高橋君が移動することを 1 回の移動として数えるので、
それぞれの移動にかかる時間が異なることに気をつけてください。

入力

入力は以下の形式で標準入力から与えられる。
N va vb L
1 行目に、高橋君の移動回数を表す整数 N(1≦N≦100) 、高橋君の秒間移動距離を表す整数 va(1≦va≦100)、
カメの秒間移動距離を表す整数 vb(1≦vb<va)、高橋君とカメの最初の距離を表す整数 L(1≦L≦100) が、
空白区切りで与えられる。

出力

カメのいる場所に高橋君が移動する動作を N 回行った後の、高橋君とカメの距離を出力せよ。
出力は標準出力におこない、末尾には改行をいれること。
出力は絶対誤差あるいは相対誤差の少なくとも片方が 10−6 以下であれば許容される。

出典

B: アキレスと亀 - AtCoder Regular Contest #012 | AtCoder

回答

AtCoder/arc012_2.php at master · wada811/AtCoder · GitHub
入力例1でよく考えれば簡単。

[競技プログラミング][PHP][AtCoder]週末

A - 週末

時間制限 : 2sec / スタック制限 : 16MB / メモリ制限 : 64MB

問題文

高橋君は、週末が大好きです。
高橋君は英語のデジタルカレンダーを使っているのですが、高橋君は英語の曜日を読むことができません。
カレンダーに表示されている曜日が与えられるので、
あと何日で週末かを計算するプログラムを作成してください。

入力

入力は以下の形式で標準入力から与えられる。
day
1 行目に day が与えられる。
day は曜日を表す文字列で Sunday(日曜日)、Monday(月曜日)、Tuesday(火曜日)、
Wednesday(水曜日)、Thursday(木曜日)、Friday(金曜日)、Saturday(土曜日) のいずれかである。

出力

週末までの日数を出力せよ。なお、週末とは、土曜日および日曜日のことを表す。
もし、すでに週末であれば、 0 と出力すること。
出力は標準出力におこない、末尾には改行をいれること。

出典

A: 週末 - AtCoder Regular Contest #012 | AtCoder

回答

AtCoder/arc012_1.php at master · wada811/AtCoder · GitHub
結果を配列を持っていて出力するだけ。
2013/01/20

[競技プログラミング][PHP][AtCoder]鉛筆リサイクルの新技術

A - 鉛筆リサイクルの新技術

時間制限 : 2sec / スタック制限 : 64MB / メモリ制限 : 256MB

問題文

世界的大手鉛筆会社のファイバーカステラ社が、
小さくなって使えなくなってしまった鉛筆を再利用する画期的な新技術を発明した。
この技術は小さくなった鉛筆 m 本から新しい鉛筆を n 本 (m > n) 作り出すものである。
ファイバーカステラ社が N 本の鉛筆を製造・販売し、その全てが使用されて回収され、
回収された使えなくなった鉛筆から新しい鉛筆を作る。
これらを販売し、やはり全てが使用後回収されて新たな鉛筆の原料となる。
これを繰り返した結果として、
ファイバーカステラ社が総計何本の鉛筆を販売できるか計算するプログラムを作成せよ。
再利用する際に、回収されたにもかかわらず新しい鉛筆の原料とされなかった鉛筆を保持しておき、
任意のタイミングで回収した鉛筆に加えても良い。
販売できる本数には、はじめの N 本も忘れずに加えること。
また、 N > m とし、m と n が互いに素であるとする。

入力

入力は以下の形式で標準入力から与えられる。 自然数 m 、 n 、 N がこの順に半角空白区切りで入力される。
m n N
1 行目には整数 m 、 n 、N が与えられる。
m は小さくなって使えなくなってしまった鉛筆の数である。
n はファイバーカステラ社が作り出す新しい鉛筆の本数である。
N はファイバーカステラ社が最初に販売する鉛筆の本数である。
(1 ≦ n < m < N ≦ 1,000) であり、m と n が互いに素であることは保証されている。

出力

ファイバーカステラ社が販売する鉛筆の総数を標準出力に 1 行で出力すること。
この数には使い終わった後に再度製造された鉛筆も含まれる。
また、出力の最後には改行をいれること。

出典

A: 鉛筆リサイクルの新技術 - AtCoder Regular Contest #011 | AtCoder

回答

AtCoder/arc011_1.php at master · wada811/AtCoder · GitHub
<?php
fscanf(STDIN, "%d %d %d", $consume, $create, $number);
$total = $number;
$rest = 0;
while($number >= $consume){
    $rest = $number % $consume;
    $number = (int)($number/ $consume) * $create;
    $total += $number;
    $number += $rest;
}
println($total);
function println($var, $line = null){
    if(is_null($line)){
        echo $var . PHP_EOL;
    }else{
        echo $line . ':' . $var . PHP_EOL;
    }
}
?>
A問題くらい飲みながらでも解けるだろーとか思ってたら
いつものA問題よりちょっと難しくてヤベってなった。
シャキッとして出力例2の解説を読みつつ組んだ。
途中で競技プログラミング用に結果出力とデバッグ出力を兼ねた関数を作り始めて、
SublimeText2 のスニペット登録してたら時間終わってた。
2013/01/10

[競技プログラミング][PHP][AtCoder]超大型連休

B - 超大型連休

時間制限 : 2sec / スタック制限 : 10MB / メモリ制限 : 64MB

問題文

2011 年、AtCoder 国の高橋首相はある重大な決定を行った。
その決定とは...法改正である。国民の祝日に関する法律を変更し、休日を増やすことにした!!
国民の創造性を尊重するその決定が、霞が関を魔境へと変えたッ!

あなたは霞が関の国土交通省に勤務する職員であり、この法改正により上司から新たな任務を与えられた。
その任務とは、2012 年の「連休の最大日数」を計算することである。
連休の大きさを事前に計算することで国民の行動を予想し、
高速道路の部分的な値下げを行い、経済を活性させるためだ。
したがって、あなたに失敗することは許されない。国民の行動を正確に予想できなくなるからだ。

以下に、「連休」の定義と諸注意を記す。
AtCoder 国が使用する暦はグレゴリオ暦に従う。
「連休」とは、「休日」が連続して続くことである。
「土曜日」「日曜日」「祝日」「振替休日」が「休日」に相当する。
「祝日」が他の休日と同じ日に指定されていた場合、「振替休日」を必ず設置する。
「振替休日」は祝日の時系列順に決定していき、その祝日以降最も近い平日(休日を除いた日)となる。
2012 年 1 月 1 日は日曜日である。

入力

入力は以下の形式で標準入力から与えられる。
N
m1/d1
m2/d2
:
:
mn/dn
1 行目には祝日の日数を表す整数 N が与えられ、 0≦N≦366 を満たす。
2 行目から N+1 行目までの N 行で祝日の日付が与えられる。
mi は i(1≦i≦366) 番目に与えられる祝日の月で、 1≦mi≦12 を満たす。
di は i(1≦i≦366) 番目に与えられる祝日の日で、
mi=2 のとき、 1≦di≦29 を満たす。
mi=(4,6,9,11) のとき、 1≦di≦30 を満たす。
mi=(1,3,5,7,8,10,12) のとき、 1≦di≦31 を満たす。
mi と di はともに整数である。
mi と di は必ず/で区切られて与えられる。
祝日は時系列順に与えられるとは限らないことに注意せよ。
ただし、同じ日付が複数与えられないことは保証されている。

出力

法改正後における 2012 年の連休の最大日数を出力せよ。
出力は標準出力におこない、末尾には改行をいれること。

出典

B: 超大型連休 - AtCoder Regular Contest #010 | AtCoder

回答

Submission #65857 - AtCoder Regular Contest #010 | AtCoder
AtCoder/arc010_2.php at master · wada811/AtCoder · GitHub
<?php
// 土日の設定
$holidays = array();
for(
    $date = DateTime::createFromFormat('Y/m/d', '2012/01/01', new DateTimeZone('Asia/Tokyo')),
    $end = DateTime::createFromFormat('Y/m/d', '2012/12/31', new DateTimeZone('Asia/Tokyo'));
    $date->diff($end)->format('%R') === '+';
    $date->add(new DateInterval('P1D'))
){
    if($date->format('w') === '0' || $date->format('w') === '6'){
        $holidays[$date->format('Y/m/d')] = true;
    }else{
        $holidays[$date->format('Y/m/d')] = false;
    }
}
// 祝日の取得・設定
fscanf(STDIN, "%d", $n);
for($i = 0; $i < $n; $i++){
    fscanf(STDIN, "%d/%d", $month, $day);
    $date = DateTime::createFromFormat('Y/m/d', "2012/{$month}/{$day}", new DateTimeZone('Asia/Tokyo'));
    // 振替休日の設定
    while($holidays[$date->format('Y/m/d')]){
        $date->add(new DateInterval('P1D'));
    }
    $holidays[$date->format('Y/m/d')] = true;
}
// 最大連休日数の取得
$max = 0;
$count = 0;
foreach($holidays as $holiday){
    if($holiday){
        $count++;
    }else{
        $count = 0;
    }
    $max = $count > $max ? $count : $max;
}
echo $max . PHP_EOL;
?>
通らなかったコード。
AtCoderの回答を見るとわかるのだが一部の問題は何故か通らない。
サンプルなどは通ってしまって何が行けないのか全くわからなくなってしまった。
それにしてもfor文の初期化部でDateTime型の変数を宣言するというのはまぁ酷い。
そしてこれは解決しなそうな感じがする。
誰かわかったらコメントお願いします。
2013/01/09

[競技プログラミング][PHP][AtCoder]名刺交換

A - 名刺交換

時間制限 : 2sec / スタック制限 : 10MB / メモリ制限 : 64MB

問題文

青木君は就職活動をおこなっている大学生で、名刺を N 枚持っています。
これから M 日間の就職活動を予定しており、 i 日目には名刺を ci 枚消費することがわかっています。
就職活動を行うにあたり、名刺が足りなくなると非常に困ります。
そこで、青木君はそれぞれの日のはじめに名刺の所持枚数を確認し、
A 枚以下ならば B 枚名刺を補充することにしました。
B 枚補充しても A 枚以下にしかならないような場合でも、それ以上の補充はできません。

最初から持っている N 枚とこのような補充で、就職活動の最後の日まで乗りきれるかどうか判定してください。
もし、足りなくなる場合は、その日付を青木君に教えてあげてください。

入力

入力は以下の形式で標準入力から与えられる。
N M A B
c1
c2
:
:
cM
1 行目に N , M , A , B が半角スペースで区切られて与えられる。
N は持っている名刺の枚数で 1≦N≦1,000 を満たす。
M は就職活動の日数で 0≦M≦100 を満たす。
A は名刺を補充するタイミングの枚数を示す数で 0≦A≦1,000 を満たす。
B は補充する名刺の枚数で 0≦B≦1,000 を満たす。
N , M , A , B は全て整数である。
2 行目から M+1 行目までの M 行間で、名刺を配る枚数がそれぞれ与えられる。
ci は i(1≦i≦M) 日目に配る名刺の枚数を表す整数で 0≦ci≦1,000 を満たす。

出力

就職活動の最後の日まで乗り切ることができればcompleteと出力すること。
もし、名刺を配りきってしまった場合は、足りなくなった日の日付を出力すること。
出力は標準出力におこない、末尾には改行をいれること。

出典

A: 名刺交換 - AtCoder Regular Contest #010 | AtCoder

回答

AtCoder/arc010_1.php at master · wada811/AtCoder · GitHub
<?php
fscanf(STDIN, "%d %d %d %d", $num, $days, $limit, $purchase);
for($i = 1; $i <= $days; $i++){
    $distribute = trim(fgets(STDIN));
    if($limit >= $num){
        $num += $purchase;
    }
    if($num >= $distribute){
        $num -= $distribute;
    }else{
        echo $i . PHP_EOL;
        exit();
    }
}
echo 'complete' . PHP_EOL;
?>
補充チェックして配布チェックして出力するだけの簡単な問題。
2012/12/11

[競技プログラミング][PHP][第2回早稲田大学プログラミングコンテスト]団子とうさぎ

A - 団子とうさぎ

時間制限 : 2sec / スタック制限 : 64MB / メモリ制限 : 64MB

古来より,月にはうさぎが住んでおり,餅をついていると言われている.
我々が月を見上げるとき,うさぎ達もまた地球を見ているのである.

問題

今,うさぎたちの目の前には N 段に積まれた団子がある.
今回の積み方では,上から x 段目には,x2 個の団子がある.
これらの団子を M 匹のうさぎたちで平等に分けることを考えよう.
あなたの仕事は,団子を平等に分けた時,いくつ団子が余るかを求めるプログラムを書くことである.

入力

入力は以下の形式で標準入力から与えられる.
N M
団子の段数を表す整数 N(1≦N≦100) とうさぎの数 M(1≦M≦100)が半角スペース区切りで与えられる.

出力

団子を平等に分けた時,余る団子の数を標準出力に 1 行で出力せよ.
なお、最後には改行を出力せよ.

出典

A: 団子とうさぎ - 第2回早稲田大学プログラミングコンテスト | AtCoder

回答

AtCoder/wupc_01.php at master · wada811/AtCoder · GitHub
<?php
fscanf(STDIN, "%d %d", $n, $rabbit);
$count = 0;
for($i = 1; $i <= $n; $i++){
    $count += pow($i, 2);
}
echo $count % $rabbit . PHP_EOL;
?>
$count を求めるもっと上手い方法あったかな?
2012/12/10

[競技プログラミング][PHP][東京大学プログラミングコンテスト2012]2012年12月02日

A - 2012年12月02日

時間制限 : 2sec / スタック制限 : 64MB / メモリ制限 : 256MB

問題

今日は2012年12月02日であって, 月日を構成する数字1202を並べ替えると西暦を表す数字2012になる.
このような日を良い日という事にする. 与えられた日付が良い日かどうかを答えよ.

入力

yyyy/mm/dd という形の文字列が一行で与えられる.
yyyyは,4桁の整数,mmは先頭が0でありうる2桁の整数,ddは先頭が0でありうる2桁の整数である.

yyyyが西暦,mmが月,ddが日を表している.

出力

与えられた日付けが良い日であるならば,yes 良い日でないならば,no を一行に出力せよ.

制約

1000≤yyyy≤9999
与えられる日付は,正しい日付を表している.

出典

A: 2012年12月02日 - 東京大学プログラミングコンテスト2012 | AtCoder

回答

AtCoder/utpc2012_01.php at master · wada811/AtCoder · GitHub
<?php
$date = trim(fgets(STDIN));
$date1 = array($date[0], $date[1], $date[2], $date[3]);
$date2 = array($date[5], $date[6], $date[8], $date[9]);
echo array_count_values($date1) == array_count_values($date2) ? 'yes' : 'no';
echo PHP_EOL;
?>
あんまり良い名前が思いつかなかったから適当な変数名。使い捨てのコード。ひどい。
2012/12/09

[競技プログラミング][PHP][DigitalArts プログラミングコンテスト2012]Password

B - Password

時間制限 : 2sec / スタック制限 : 64MB / メモリ制限 : 64MB

問題文

セキュリティに興味がある高橋君は、デジタルアーツ株式会社に就職したい青年です。
そこで、高橋君は自分が運営するサービスであるAtCoderのセキュリティについて見なおしてみることにしました。

現在AtCoderのシステムでは、パスワードは 1 文字以上 20 文字以下の英小文字のみとしています。
そして、文字列 s に対してハッシュ値( hash(s) )を求める以下の式があり、
パスワードと入力した文字列に対して、それぞれこの式で算出したハッシュ値が一致すると、
入力した文字列は正しいとみなします。

hash(s)=Num(c1)+Num(c2)+…+Num(cN) (ci は文字列 s の i 番目の文字を意味する)

なお、上記の式の関数 Num() とは英小文字を数字に変換する関数で、
Num(a)=1, Num(b)=2, …., Num(z)=26 というように、
a から z に対して順に 1 から 26 までの数字を返します。

高橋君は、このシステムではパスワードと違う文字列でも
簡単にハッシュ値が一致してしまうことに気づきました。
例えば、文字列 abc のハッシュ値は、1+2+3=6 となりますが、
文字列 bbb のハッシュ値も 2+2+2=6 ですし、f も 6 になってしまいます。

高橋君は、現在使っているパスワードに対してどのような文字列が
正しいパスワードとして認識されてしまうか知りたいです。
正しいパスワード以外で条件を満たすものを 1 つ出力しなさい。
条件を満たすものが複数ある場合は、どの文字列を出力しても構いません。
もし条件を満たすパスワードが存在しない場合は NO と出力しなさい。
なお、AtCoderのシステムで入力できるパスワードは 1 文字以上 20 文字以下の英小文字のみなので、
答えとして出力する文字列もその条件をみたします。

入力

入力は以下の形式で標準入力から与えられる。
c1c2‥‥cN
入力には正しいパスワードを表す長さ N(1≦N≦20) の文字列が 1 行で与えられる。
正しいパスワードの i 番目の文字を表す ci は英小文字 (a-z) である。

出力

与えられた正しいパスワードを表す文字列と等しいハッシュ値になる英小文字 1 文字以上 20 文字以下の文字列を、
正しいパスワード以外のいずれか 1 つ出力せよ。
また、そのような文字列が存在しない場合は NO と出力せよ。
なお、出力は 1 行のみとし、最後には改行を出力せよ。

出典

B: Password - DigitalArts プログラミングコンテスト2012 | AtCoder

回答

AtCoder/digitalarts_2.php at master · wada811/AtCoder · GitHub
<?php
$password = trim(fgets(STDIN));
$ascii_a = ord('a') - 1;
$numbars = array();
for($i = 0, $end = strlen($password); $i < $end; $i++){
    $numbars[] = ord($password[$i]) - $ascii_a;
}

$sum = array_sum($numbars);
// special case
if($sum === 1 || $sum === 26 * 20){
    // 'a' or 'zzzzzzzzzzzzzzzzzzzz'
    echo 'NO' . PHP_EOL;
    exit();
}

$answers = array();
if(count($numbars) === 1){
    // 'b' - 'z'
    $answers[0] = $numbars[0] - 1;
    $answers[1] = 1;
}else{
    while($sum){
        $answers[] = min(26, $sum);
        $sum -= min(26, $sum);
    }
}
// ex. 'za' -> 'az'
while($answers == $numbars){
    shuffle($answers);
}

$another_password = '';
for($i = 0, $count = count($answers); $i < $count; $i++){
    $another_password .= chr($answers[$i] + $ascii_a);
}
echo $another_password . PHP_EOL;
?>
2行目でtrimし忘れて意図しない改行コードで答えが合わなくて泣きそうになった。
文字列を配列でアクセスできるので1文字ずつアスキーコード変換して配列に入れる。
答えが NO になる例外を弾き、1文字のパスワードとそうでないもので処理を分ける。
後は28行目に書いたようなやつのためにshuffleして入れ替えておく。コレで確実。
あとはアスキーコードを逆変換して出力!
2012/12/08

[競技プログラミング][PHP][DigitalArts プログラミングコンテスト2012]C-Filter

A - C-Filter

時間制限 : 2sec / スタック制限 : 64MB / メモリ制限 : 64MB

問題文

セキュリティに興味がある高橋君は、デジタルアーツ株式会社に就職したい青年です。
彼は、面接で自分をアピールするために、
得意なプログラミングでフィルタリングソフト「C-Filter」を作ろうと考えています。
「C-Filter」は、与えられた文字列 s に、
あらかじめ登録しておいた「NGワード」と一致する文字列が存在する場合、
その文字列の長さと等しい数の *に置き換えて出力するソフトウェアです。
この文字列を置き換える処理をフィルタリングと呼びます。
「NGワード」とは、半角英小文字と*から構成されます。
*はすべての半角英小文字 1 文字と一致します。
例えば myonmyon は、NGワードmyo*myonと一致します。
ただし、NGワードは単語ごとに適用されるため、myo myon はNGワード myo*myonとは一致しません。
また、NGワードはある単語に対して完全に一致する必要があります。
例えば、abcdeは、NGワードabcやbcd、cdeに一致しません。

文字列 s と、NGワードが与えられるので、C-Filterの出力する文字列を答えてください。

入力

入力は以下の形式で標準入力から与えられる。
s
N
t1
:
:
tN
入力は N+2 行からなる。
1 行目には 1 文字以上 1,000 文字以下の文字列 s が与えられる。
s はフィルタリングする対象の文字列を半角スペースで区切って繋げた文字列である。
2 行目にはNGワードの個数を表す整数 N(0≦N≦50) が与えられる。
3 行目から N+2 行目までNGワードを表す文字列 ti(1≦i≦N)が与えられる。
文字列 ti は半角英小文字と * から構成される。
文字列 ti の長さは 1 文字以上、 20 文字以下である。
文字列 ti に含まれる * は、半角スペースを除くすべての半角英小文字 1 文字をフィルタリングの対象とします。

出力

入力された文字列 s をC-Filterでフィルタリングした結果を 1 行で出力せよ。
なお、最後には改行を出力せよ。

出典

A: C-Filter - DigitalArts プログラミングコンテスト2012 | AtCoder

回答

AtCoder/digitalarts_1.php at master · wada811/AtCoder · GitHub
<?php
$sentence = trim(fgets(STDIN));
fscanf(STDIN, "%d", $n);
$filters = array();
for($i = 0; $i < $n; $i++){
    $filters[] = '/^' . str_replace('*', '.', trim(fgets(STDIN))) . '$/';
}
$words = explode(' ', $sentence);
$censored = preg_replace_callback(
                $filters,
                create_function(
                    '$matches',
                    'return str_repeat("*", strlen($matches[0]));'
                ),
                $words
            );
echo implode(' ', $censored) . PHP_EOL;
?>
fgetsで1行取得。この際にtrimしておかないと思わぬ改行コードでハマる。
6行目で取得しつつ正規表現に入れ込む。 間の空白を置換しないようにするのが面倒なので空白で分割。 preg_replaceのe修飾子が PHP 5.5.0 で非推奨になったので
AtCoder の PHP のバージョンは 5.3.6だけど使わないでおく。
代わりにpreg_replace_callbackでうまいことやる。
第二引数のコールバック関数にマッチした文字の文字数だけstr_repeatで繰り返す。
後は文字列に直して出力!テクニカルな感じでカッコいい!
2012/10/28

[競技プログラミング][PHP][AtCoder]おとぎの国の高橋君

B - おとぎの国の高橋君

時間制限 : 2sec / スタック制限 : 10MB / メモリ制限 : 64MB

問題文

高橋君の住むAtCoder国では、
私達が普段使用する数字と同様に 10 個のアラビア数字 (0−9) の 10 進数が使われています。
しかし、私達が普段使用する数字は大小関係が 0<1<2<3<4<5<6<7<8<9 の順になっているのに対して、
AtCoder国の数字ではその大小関係が異なっています。
例えば、AtCoder国の数字では 0<9<8<7<6<5<4<3<2<1 の順になっている場合、
AtCoder国では 9 よりも 8 の方が大きいことになります。また、97 よりも 72 の方が大きいことになります。

AtCoder国の数字の大小関係といくつかの数が与えられるので、
AtCoder国の数字の大小関係で昇順に並び替えてください。
なお、私達が普段使用する数字同様、AtCoder国で最も小さい数字は 0 であることは決まっています。

入力

入力は以下の形式で標準入力から与えられる。
b0 b1 ‥‥ b9
N
a0
a1
:
:
aN−1
入力は N+2 行ある。
1 行目には、AtCoder国での 1 桁の数字の大小関係が与えられる。
AtCoder国では b0<b1<…<b9 であることを表している。
b0 は必ず 0 である。
重複する数字は存在せず、0 から 9 までの数字が 1 度ずつ現れる。
2 行目には並び替える数の個数を表す整数 N(1≦N≦777) が与えられる。
3 行目からの N 行には、j+3 行目に並び替える数を表す整数 aj(1≦aj≦777,777,777) が与えられる。

出力

与えられた数をAtCoder国の数字の大小関係にあわせて昇順に並び替え、
標準出力に 1 行に 1 つの数字ずつ出力せよ。
なお、最後には改行を出力せよ。

出典

B: おとぎの国の高橋君 - AtCoder Regular Contest #009 | AtCoder

回答

AtCoder/arc009_2.php at master · wada811/AtCoder
<?php
// デバッグ用関数
function echos($array, $line){
    echo $line . '::k: ' . implode(' ', array_keys($array)) . PHP_EOL;
    echo $line . '::v: ' . implode(' ', $array) . PHP_EOL;
}
// 変換処理はここから
$cardinalNumber = trim(fgets(STDIN));
$cardinalNumbers = explode(' ', $cardinalNumber);
fscanf(STDIN, '%d', $n);
for($i = 0; $i < $n; $i++){
    fscanf(STDIN, '%d', $targetNumbers[]);
}
// echos($targetNumbers, __LINE__);
// 14::k: 0 1 2 3 4 5 6 7 8 9
// 14::v: 1 2 3 4 5 6 7 8 9 10
// targetNumbers の数字をアルファベットに置換
$alphabets = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j');
$numbers = array_keys($alphabets);
$targetAlphabets = str_replace($numbers, $alphabets, $targetNumbers);
// echos($targetAlphabets, __LINE__);
// 21::k: 0 1 2 3 4 5 6 7 8 9
// 21::v: b c d e f g h i j ba
// このアルファベットを cardinalNumbers の順になるようにソート
$cardinalAlphabets = array();
for($i = 0, $end = count($cardinalNumbers); $i < $end; $i++){
    $cardinalAlphabets[$i] = $alphabets[$cardinalNumbers[$i]];
}
// echos($cardinalAlphabets, __LINE__);
// 29::k: 0 1 2 3 4 5 6 7 8 9
// 29::v: a i b d f e j h g c
// targetAlphabets を cardinalAlphabets の k に置換
$numbers = array_keys($cardinalAlphabets);
$atCoderNumbers = str_replace($cardinalAlphabets, $numbers, $targetAlphabets);
// echos($atCoderNumbers, __LINE__);
// 35::k: 0 1 2 3 4 5 6 7 8 9
// 35::v: 2 9 3 5 4 8 7 1 6 20
// この数値を昇順に並べ替える
asort($atCoderNumbers);
// echos($atCoderNumbers, __LINE__);
// 40::k: 7 0 2 4 3 8 6 5 1 9
// 40::v: 1 2 3 4 5 6 7 8 9 20
// この順の k で targetNumbers を得る
$results = array();
$orders = array_keys($atCoderNumbers);
foreach($orders as $order){
    $results[] = $targetNumbers[$order];
}
// echos($results, __LINE__);
// 49::k: 0 1 2 3 4 5 6 7 8 9
// 49::v: 8 1 3 5 4 9 7 6 2 10
// 出力!
echo implode(PHP_EOL, $results) . PHP_EOL;
?>
変換してソートして逆変換するだけだとか思ってやってみたら結構大変なことになった。
数字をAtCoder国の大小関係の数字に変換しようとしたら
str_replaceで配列を渡すと多重変換されてしまうようだった。
仕方ないので適当にアルファベットを間に挟んで変換していったら混乱したので
デバッグ用関数を作ったら結構すんなり解けた。
間にアルファベットを挟む分だけわかりにくくなっているが、
変換してしまえばasortでソートするだけだし、
ソートすればインデックスがわかるので逆変換しなくても
元の数字をそのインデックスの順に出力するだけで良い。

普段よりB問題から面倒くさかったかも。C問題も難しかったし。
2012/10/27

[競技プログラミング][PHP][AtCoder]元気にお使い!高橋君

A - 元気にお使い!高橋君

時間制限 : 2sec / スタック制限 : 10MB / メモリ制限 : 64MB

問題文

ある日高橋君はお母さんに近くのスーパーまでおつかいを頼まれました。
お母さんに手渡されたおつかいメモには、買ってきて欲しい商品の値段と個数がそれぞれ書かれています。
ただしメモに書かれている値段には消費税が含まれていませんが、全ての商品には消費税が 5% かかります。
高橋君のおつかいに必要な金額を答えなさい。
なお、消費税は 1 円未満は切り捨てます。

入力

入力は以下の形式で標準入力から与えられる。
N
a0 b0
a1 b1
:
:
aN−1 bN−1
入力は N+1 行ある。
1 行目には、購入する商品の品数を表す整数 N(1≦N≦10) が与えられる。
2 行目から N 行の i+2 行目にはある商品の購入したい個数を表す整数 ai(1≦ai≦10) とその単価を表す整数 bi(1≦bi≦1,000) が空白区切りで与えられる。

出力

高橋君のおつかいに必要な金額を標準出力に 1 行で出力せよ。
なお、最後には改行を出力せよ。

出典

A: 元気にお使い!高橋君 - AtCoder Regular Contest #009 | AtCoder

回答

AtCoder/arc009_1.php at master · wada811/AtCoder · GitHub
<?php
fscanf(STDIN, '%d', $n);
$sum = 0;
for($i = 0; $i < $n; $i++){
    fscanf(STDIN, '%d %d', $num, $price);
    $sum += $num * $price;
}
$sum *= 1.05;
echo (int)$sum . PHP_EOL;
?>
個数と単価をかけて合計し、最後に消費税をかけて
小数点以下を切り捨てるだけの簡単なプログラム。
最初切り捨てを読んでなくて WA になったけど。
2012/10/03

[競技プログラミング][PHP][K2PC Easy]ハンバーガー

A - ハンバーガー(Hamburger)

時間制限 : 1sec / スタック制限 : 128MB / メモリ制限 : 256MB

問題文

kyuridenamidaは, K2PCというレストランでバイトをしている.
彼は受付を担当しているが, 時給が810円と低いので, 時給が893円の厨房を担当したいと考えている.

厨房では, K2PCレストランの(唯一の商品であり)目玉商品であるK2PCハンバーガーを作る仕事が行われている.
ハンバーガーを1個作るには, 肉が1枚, パンが2枚, トッピング類が3個必要である.
今, 肉がa枚, パンがb枚, トッピング類がc個あるとする.
ハンバーガーをN個作るためには, それぞれの材料が残りいくつ必要か求めよ.

入力

a b c
N
1行目に, 今ある肉の枚数, パンの枚数, トッピングの個数を表す整数がこの順に入力される.
2行目に, 作りたいハンバーガーの個数を表す整数が入力される.

出力

それぞれの材料がいくつ必要かを, 肉の枚数, パンの枚数, トッピングの個数の順でスペース区切りで出力せよ.
改行を忘れないように注意すること.

出典

A: ハンバーガー(Hamburger) - Kyuride Kagamiz Programming Contest (Easy) | AtCoder

解答

AtCoder/k2pc_easy_1.php at master · wada811/AtCoder · GitHub
<?php
fscanf(STDIN, "%d %d %d", $a, $b, $c);
fscanf(STDIN, "%d", $n);
$n_a = max($n * 1 - $a, 0);
$n_b = max($n * 2 - $b, 0);
$n_c = max($n * 3 - $c, 0);
echo $n_a . ' ' . $n_b . ' ' . $n_c . PHP_EOL;
?>
なんかやってたから解いてみたけどこれくらいしか解けなかった。
次の問題のビットマニアは問題がまずよくわからなかったし読むのが面倒だった。
2012/09/28

[競技プログラミング][PHP][AtCoder]謎のたこ焼きおじさん

B - 謎のたこ焼きおじさん

時間制限 : 2sec / スタック制限 : 10MB / メモリ制限 : 64MB

問題文

あなたはたこ焼きを買いに来たところ、伝説のたこ焼きマスター高橋社長に認められ、
新しく作るたこ焼き屋さんの店長を任されました。
店長に任命されたあなたに与えられた最初の仕事は、お店の看板を作成することでした。
ところが高橋社長は使えない時間がないので、
たこ焼き屋さんの名前は決めましたが、看板を作るのはあなたに任せて去って行きました。
その際に看板を作るための英字ブロックが入ったいくつかのキットを渡されました。
英字キットとは、ランダムな英字ブロックが含まれている袋のことです。
例えば英字キットを表す文字列が ABCC であるような英字キットの場合、
  • 英字ブロックAが1つ含まれている。
  • 英字ブロックBが1つ含まれている。
  • 英字ブロックCが2つ含まれている。
とみなすことができます。
つまり、英字キット ABCC 1 袋につき、
英字ブロックAと英字ブロックBを1つずつ、英字ブロックCを2つを看板に用いることができます。
高橋社長から渡された英字キットは全て同じ英字キットだったので、どのキットを開けても中身は全く同じです。
あなたは看板にお金をかけるわけにもいかないので、開封する英字キットを最小にして残りは返品したいです。
どれだけの英字キットを使うことで、お店の看板を作ることができるでしょうか。

入力

入力は以下の形式で標準入力から与えられる。
N M
name
kit
入力は 3 行ある。
1 行目にお店の名前の文字数 N (1≦N≦50) と、
英字キット 1 袋に入ってる英字ブロックの数 M (1≦M≦50) が空白区切りで与えられる。
2 行目にはお店の名前を表す文字列 name が N 文字で与えられる。
文字列 name に含まれる文字は A-Z のみである。
3 行目には英字キットに含まれている英字ブロックを表す文字列 kit が M 文字で与えられる。
文字列 kit に含まれる文字は A-Z のみである。

出力

看板をつくるために必要な英字キットの最小数を標準出力に 1 行で出力すること。
与えられた英字キットで看板を作成することができない場合は、-1を出力すること。
また、出力の最後には改行をいれること。

出典

B: 謎のたこ焼きおじさん - AtCoder Regular Contest #008 | AtCoder

回答

AtCoder/arc008_2.php at master · wada811/AtCoder · GitHub
<?php
fscanf(STDIN, "%d %d", $n, $m);
for($i = 0; $i < $n; $i++){
    $name[] = fgetc(STDIN);
}
fgetc(STDIN); // throw line feed code
for($i = 0; $i < $m; $i++){
    $kit[] = fgetc(STDIN);
}
fgetc(STDIN); // throw line feed code

$name_counts = array_count_values($name);
$kit_counts = array_count_values($kit);
foreach($name_counts as $key => $name_count){
    if(array_key_exists($key, $kit_counts)){
        $diff[] = max($name_count - $kit_counts[$key], 0);
    }else{
        $count = -1;
        break;
    }
}
$count = isset($count) ? $count : max($diff) + 1;
echo $count.PHP_EOL;
?>
一文字ずつ配列に保存して改行コードを捨てるのが10行目まで。
array_count_valuesで配列の値の数を数えて配列化したら
店名に含まれるアルファベットが英字キットに含まれるかarray_key_existsで探す。
見つからなかったら店名は完成しないのでカウントを -1 にして break する。
見つかったら差をとる。マイナスになったら0にして配列に詰める。
差の最大をmaxでとるが、差なので英字キット1袋分カウントが減るから+1する。

抜けは無さそうなんだけど AC にならないケースがあるみたいなんだよなぁ。
どなたかわかる方コメントや @wada811 にリプライください。お願いします。

追記(2012/09/29): ACになりました。

<?php
fscanf(STDIN, "%d %d", $n, $m);
for($i = 0; $i < $n; $i++){
    $name[] = fgetc(STDIN);
}
fgetc(STDIN); // throw line feed code
for($i = 0; $i < $m; $i++){
    $kit[] = fgetc(STDIN);
}
fgetc(STDIN); // throw line feed code
 
$name_counts = array_count_values($name);
$kit_counts = array_count_values($kit);
foreach($name_counts as $key => $name_count){
    if(array_key_exists($key, $kit_counts)){
        $counts[] = ceil($name_count / $kit_counts[$key]);
    }else{
        $count = -1;
        break;
    }
}
$count = isset($count) ? $count : max($counts);
echo $count.PHP_EOL;
?>
引き算でキット数を計算するのが問題だったようです。
コメントくださったAyaka Kashiyamaさんありがとうございました。
英字毎にカウントは独立しているので考えるべきは以下のパターンくらいだったでしょうか。
(name, kit) = ('A', ''), ('', 'A'), ('A', 'A'), ('AA', 'A'), ('A', 'AA'), ('AAA', 'AA')
1番目は キットに存在しないので -1, 2番目は存在しない英字なのでチェックが行われない, 3番目は 1/1 で 1,
4番目は 2/1 で 2, 5番目は 1/2 で ceilで切り上げて 1, 6番目は 3/2 で 同様に切り上げて 2 になる。
うーん。悔しい。反例を見つける力が足りない。悔しい。
2012/09/27

[競技プログラミング][PHP][AtCoder]たこ焼き買えるかな?

A - たこ焼き買えるかな?

時間制限 : 2sec / スタック制限 : 10MB / メモリ制限 : 64MB

問題文

ある日、あなたはたこ焼きを買いに行きました。
そのお店ではたこ焼き 1 個 15 円、 10 個まとめて 100 円で売っています。
今、あなたは N 個のたこ焼きを買おうと思っています。
あなたはたこ焼きを N 個以上買うのに少なくともいくら必要でしょうか。

入力

入力は以下の形式で標準入力から与えられる。
N
入力は 1 行ある。
1 行目には、購入したいたこ焼きの個数を表す整数 N(1≦N≦50) が与えられる。

出力

たこ焼きを N 個以上買うのに必要な最小金額を標準出力に 1 行で出力すること。
なお、最後には改行を出力せよ。

出典

A: たこ焼き買えるかな? - AtCoder Regular Contest #008 | AtCoder

回答

AtCoder/arc008_1.php at master · wada811/AtCoder · GitHub
<?php
fscanf(STDIN, "%d", $n);
$cost = (int)($n / 10) * 100 + min($n % 10 * 15, 100);
echo $cost.PHP_EOL;
?>
7個買うならセットで買うほうがお得!って言ってセット買うとは思わなかった。
いまいちminって使い慣れてなくて三項演算子で書いてしまう…。
もうC言語のように書かなくていいのにね…。
2012/09/20

[競技プログラミング][PHP][AtCoder]迷子のCDケース

B - 迷子のCDケース

時間制限 : 2sec / スタック制限 : 10MB / メモリ制限 : 64MB

問題文

高橋君はCDで音楽を聴くことが好きです。
CDプレイヤーには先日聴いたCDが入ったままになっているのですが、
そのCDに対応するCDケースが見当たらないことに気づきました。
前回に聴いた時にCDケースをどこに置いたのか、残念ながら高橋君は全く思い出せませんでした。
仕方がないので高橋君は今から聴こうとしているCDをケースから取り出し、
CDプレイヤーに入っていたCDをそのケースへと片付けることにしました。
さらに別のCDを新たにCDプレイヤーに入れる場合も、
CDプレイヤーに入っていたCDは空いたCDケースに片づけます。
例えば図 1(※省略)のように 3 枚のCDがある状態で、
黄緑色のCD、オレンジ色のCDの順でCDを聴くと、
それぞれのCDは最も下の図(※省略)のように片付けられることになります。

高橋君が音楽を聴き終わった後、今日聞いたCDのリストが与えられるので、
高橋君が所持するCDケースにはそれぞれどのCDが入っているかを答えなさい。

入力

入力は以下の形式で標準入力から与えられる。
N M
disk0
disk1
:
:
diskM−1
入力は M+1 行ある。
1 行目には、高橋君が所持するCDケースの個数を表す整数 N(1≦N≦100)と、
今日聴いたCDの枚数 M(0≦M≦100) が空白区切りで与えられる。
CDケースを 1 つ無くしているので、高橋君は計 N+1 枚のCDを所持している。
CDと対応するCDケースにはそれぞれ 0 から N までの数が番号付けられている。
現在CDプレイヤーに入ってるCDとそれに対応する見当たらないCDケースは 0 番である。
2 行目からの M 行には今日聴いたCDの番号のリストが与えられる。
i+2 行目の整数 disk i (0 ≦ i ≦ M−1, 0 ≦ disk i ≦ N) は i+1 番目に disk i 番のCDを聴いたことを表している。

出力

1 から N 番までのCDケースに入ってるCDの番号を順に標準出力に 1 行に 1 ケース分ずつ出力せよ。
なお、最後には改行を出力せよ。

出典

B: 迷子のCDケース - AtCoder Regular Contest #007 | AtCoder

回答

AtCoder/arc007_2.php at master · wada811/AtCoder · GitHub
<?php
fscanf(STDIN, "%d %d", $n, $m);
for($i = 0; $i < $m; $i++){
    fscanf(STDIN, "%d", $playlist[]);
}
$now = 0;
for($i = 1; $i <= $n; $i++){
    $case[] = $i;
}
for($i = 0; $i < $m; $i++){
    if($now !== $playlist[$i]){
        $next = array_search($playlist[$i], $case);
        list($case[$next], $now) = array($now, $case[$next]);
    }
}
for($i = 0; $i < $n; $i++){
    echo $case[$i].PHP_EOL;
}
?>
6行目で now playing な CD を定義、8行目で CD ケースに CD を詰める。
プレイリストが現在再生中の CD 以外だったら入れ替え。
コレをしないと array_search で false が返ってきてしまう。
13行目は list を使ったテクニカルな変数のスワップ(入れ替え)方法。
普通に $tmp = $a; $a = $b; $b = $tmp; の円環の順で書く方法でも良かったのだけど
PHP でスワップするのは初めてだったのでなんかあるかなと調べてみたらあった。
初見の人は何やってるかわからなくて戸惑いそうだから使いにくい感じはあるけど…。

ちなみに now playing を一緒に配列に入れても解ける。
Submission #44496 - AtCoder Regular Contest #007 | AtCoder
CD を主体にするか、ケースを主体にするかの違いなんだけど
あんまりセマンティックな感じがしないので不採用でした。まぁ好みの問題ですね。
2012/09/19

[競技プログラミング][PHP][AtCoder]帰ってきた器物損壊!高橋君

A - 帰ってきた器物損壊!高橋君

時間制限 : 2sec / スタック制限 : 10MB / メモリ制限 : 64MB

問題文

高橋君はある日、コンピューターのキーボードの中の 1 つのキーが壊れていることに気づきました。
壊れたキーは押しても文字が出力されません。
力が強いことで有名な高橋君なので、キーを強く叩きすぎたのでしょう。
しかし、高橋君は小さいことは気にしない性格なので、そのキーボードを壊れたまま使うことにしました。
高橋君がタイピングする文字列が与えられるので、
壊れたキーボードを用いてタイピングした場合の出力結果を答えなさい。

入力

入力は以下の形式で標準入力から与えられる。
X
s
入力は 2 行ある。
1 行目には、壊れたキーを表す文字 X が与えられる。
X は英字の小文字(a-z)のいずれかである。
2 行目にはタイピングする文字列を表す 1 文字以上 50 文字以下の文字列が与えられる。
文字列は英語の小文字(a-z)のみで成り立っている。

出力

壊れたキーでの入力は出力されない状態で文字列をタイピングした場合に、
出力される文字列を標準出力に 1 行で出力せよ。
なお、最後には改行を出力せよ。
もし何も出力されない場合は改行のみを出力せよ。

出典

A: 帰ってきた器物損壊!高橋君 - AtCoder Regular Contest #007 | AtCoder

回答

AtCoder/arc007_1.php at master · wada811/AtCoder · GitHub
<?php
fscanf(STDIN, "%c", $key);
fscanf(STDIN, "%s", $string);
$string = str_replace($key, '', $string);
echo $string.PHP_EOL;
?>
簡単な文字列置換でクリア。
2012/09/17

[競技プログラミング][PHP][天下一プログラマーコンテスト2012]ロイヤルストレートフラッシュ

B - ロイヤルストレートフラッシュ

時間制限 : 2sec / スタック制限 : 10MB / メモリ制限 : 64MB

問題文

シャッフルされたトランプの山札が与えられる。
ここからロイヤルストレートフラッシュを作りたい。
山札からカードを 1 枚ずつめくり、手札に入れるか捨てるという操作を繰り返す。
最短でロイヤルストレートフラッシュが完成したときの捨て札を、カードを捨てた順に出力せよ。
なお、初期状態で手札は空とし、ロイヤルストレートフラッシュが完成したとき、
手札に余分なカードが存在してはならない。


与えられる山札を表す文字列は、以下のBNFに従う。

<山札> ::= <カード> | <山札> <カード>
<カード> ::= <マーク> <番号>
<マーク> ::= "S" | "H" | "D" | "C"
<番号> ::= "A" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "10" | "J" | "Q" | "K"
ロイヤルストレートフラッシュとは、同じマークの「10、J、Q、K、A」の組み合わせのことを言う。
すなわち、「S10, SJ, SQ, SK, SA」、「H10, HJ, HQ, HK, HA」、「D10, DJ, DQ, DK, DA」、
「C10, CJ, CQ, CK, CA」のカードの組み合わせ 4 種類がロイヤルストレートフラッシュである。

入力

入力は以下の形式で標準入力から与えられる。
s
山札のカードを表す文字列 s が 1 行で与えられる。
山札にはマークと番号の両方が等しいカードの組は存在しない。
ロイヤルストレートフラッシュを作ることが可能であることが保証される。

出力

ロイヤルストレートフラッシュを最短で作ったときの捨て札を表す文字列を 1 行で出力せよ。
捨て札はカードを捨てた順に記述する必要がある。
捨て札が無い場合は、 0 (数字のゼロ) を出力せよ。
なお、行の終端には改行が必要である。

出典

B: ロイヤルストレートフラッシュ - 天下一プログラマーコンテスト2012 予選C | AtCoder

回答

AtCoder/tenka1_2012_C_2.php at master · wada811/AtCoder · GitHub
<?php
fscanf(STDIN, "%s", $input);
preg_match_all('/([SHDC])([^SHDC]{1,2})/', $input, $matches);
$rsf = array('10', 'J', 'Q', 'K', 'A');
$cards = array('S' => 0, 'H' => 0, 'D' => 0, 'C' => 0);
for($i = 0; $i < count($matches[0]); $i++){
    $card = $matches[0][$i];
    $mark = $matches[1][$i];
    $num = $matches[2][$i];
    if(in_array($num, $rsf)){
        $cards[$mark]++;
    }
    if($cards[$mark] === 5){
        foreach($rsf as $number){
            $input = implode('.', explode($mark.$number, $input));
        }
        $input = explode('.', $input);
        array_pop($input);
        $trash = implode($input);
        break;
    }
}
$trash = empty($trash) ? '0' : $trash;
echo $trash.PHP_EOL;
?>
ロイヤルストレートフラッシュ以外の手札を捨てる処理がイマイチ。
ロイヤルストレートフラッシュをexplodeimplodeでドット(任意の文字)に置換。
explodeで再び配列にして、array_popで揃った後のカードを除去。
implodeで文字列に直して空チェックしたら出力。

explode/implode祭りすぎる。なんとかならんもんかなー。
2012/09/13

[競技プログラミング][PHP][天下一プログラマーコンテスト2012]与えられた数より小さい素数の個数について

A - 与えられた数より小さい素数の個数について

時間制限 : 2sec / スタック制限 : 10MB / メモリ制限 : 64MB

問題文

素数とは、1 と自分自身以外に正の約数を持たない、1 以外の自然数のことをいいます。

自然数 n が与えられるので、 n よりも小さい素数の数は何個存在するかを求めてください。

入力

入力は以下の形式で標準入力から与えられる。
n
自然数 n ( 1≤n≤10,000 ) が 1 行で与えられる。

出力

n よりも小さい素数の個数を標準出力に 1 行で出力せよ。
なお、行の終端には改行が必要である。

出典

A: 与えられた数より小さい素数の個数について - 天下一プログラマーコンテスト2012 予選C | AtCoder

回答

AtCoder/tenka1_2012_C_1.php at master · wada811/AtCoder · GitHub
<?php
fscanf(STDIN, "%d", $n);
$isPrime[0] = $isPrime[1] = 0;
for($i = 2; $i < $n; $i++){
    $isPrime[$i] = 1;
}
for($i = 0; $i < $n; $i++){
    if($isPrime[$i] === 1){
        for($j = 2 * $i; $j < $n; $j += $i){
            $isPrime[$j] = 0;
        }
    }
}
$count = array_count_values($isPrime);
echo max(0, $count[1]).PHP_EOL;
?>
AOJ/vol0/AOJ0009.cpp at master · wada811/AOJ
AOJでC言語で書いた素数判定をPHPに書きなおすだけの簡単なお仕事。
array_count_valuesで素数フラグをカウントしてやればOKだけど
単純に出力すると2ケースだけテスト落ちる。
$n に 0 か 1 を渡されると出力するものがないので
maxで最低でも 0 を出力するようにするとちゃんと通る。
2012/09/12

[競技プログラミング][PHP][天下一プログラマーコンテスト2012][AC] camel_case

[競技プログラミング][PHP][天下一プログラマーコンテスト2012]camel_case | DevAchieveの解き直し版。
記事で分かる人教えてくださいってお願いしておいたら本当に教えてくださる方が!感謝です!


回答

AtCoder/tenka1_2012_B_2_preg_replace.php at master · wada811/AtCoder
<?php
$variable = trim(fgets(STDIN));
if(isCamelCase($variable)){
    $variable = toSnakeCase($variable);
}else if(isSnakeCase($variable)){
    $variable = toCamelCase($variable);
}
echo $variable.PHP_EOL;


function isCamelCase($str){
    return preg_match('/^_*[a-z][a-z0-9]*([A-Z][a-z0-9]*)*_*$/', $str, $matches);
}

function toCamelCase($str){
    preg_match('/^(_*).*$/', $str, $matches);
    $str = substr($str, strlen($matches[1]));
    return $matches[1].preg_replace('/_([a-z])/e', 'strtoupper("$1")', $str);
}

function isSnakeCase($str){
    return preg_match('/^_*[a-z][a-z0-9]*(_[a-z][a-z0-9]*)*_*$/', $str, $matches);
}

function toSnakeCase($str){
    return preg_replace('/([A-Z])/e', '"_".strtolower("$1")', $str);
}
?>
AtCoder/tenka1_2012_B_2_preg_replace_callback.php at master · wada811/AtCoder
<?php
$variable = trim(fgets(STDIN));
if(isCamelCase($variable)){
    $variable = toSnakeCase($variable);
}else if(isSnakeCase($variable)){
    $variable = toCamelCase($variable);
}
echo $variable.PHP_EOL;


function isCamelCase($str){
    return preg_match('/^_*[a-z][a-z0-9]*([A-Z][a-z0-9]*)*_*$/', $str, $matches);
}

function toCamelCase($str){
    preg_match('/^(_*).*$/', $str, $matches);
    $str = substr($str, strlen($matches[1]));
    return $matches[1].preg_replace_callback(
        '/_([a-z])/',
        create_function('$matches', 'return strtoupper($matches[1]);'),
        $str);
}

function isSnakeCase($str){
    return preg_match('/^_*[a-z][a-z0-9]*(_[a-z][a-z0-9]*)*_*$/', $str, $matches);
}

function toSnakeCase($str){
    return preg_replace_callback(
        '/([A-Z])/', 
        create_function('$matches', 'return "_".strtolower($matches[1]);'),
        $str);
}
?>
無事に両方通りました。原因としては単語が一文字でもOKだったということ。
ただ、一文字だけの単語を許容しても置換で preg_replace と preg_replace_callback は
_aXYZ0_ を見た際に aX の後は xY ではなく、 YZ を見に行ってしまうので
マッチ方法を変えなければいけませんでした。
判定でガチガチにキャメルケースとスネークケースを判別しているので変換では少し緩めにしました。
キャメルケース → スネークケース の場合は、大文字がマッチした時点で単語の先頭だということになります。
スネークケース → キャメルケース の場合は、先頭に付加されるアンダースコアが判定に影響するので
function toCamelCase($str){
    preg_match('/^(_*).*$/', $str, $matches);
    $str = substr($str, strlen($matches[1]));
    return $matches[1].preg_replace_callback(
        '/_([a-z])/',
        create_function('$matches', 'return strtoupper($matches[1]);'),
        $str);
}
先頭のアンダースコアをマッチさせて除去した後に置換をかけて、
プレフィックスとしてアンダースコアを付加することにしました。

おかげさまで解くことができました。@cielavenirさん、本当にありがとうございました!
2012/09/02

[競技プログラミング][PHP][天下一プログラマーコンテスト2012]camel_case

B - camel_case

時間制限 : 2sec / スタック制限 : 10MB / メモリ制限 : 64MB

問題文

以下の通りに、「キャメルケースの文字列」と「アンダースコア区切りの文字列」を定めます。

単語
英小文字で始まる、英小文字・数字から成る文字列
キャメルケースの文字列
1つ以上の単語を、最初の単語以外の先頭の文字を大文字にして連結した文字列
アンダースコア区切りの文字列
1つ以上の単語を、単語の間を1つのアンダースコアで区切って連結した文字列
また、キャメルケースの文字列とアンダースコア区切りの文字列には、
連結した後、最初や最後に1つ以上のアンダースコアが追加されることがあります。

文字列が与えられるので、キャメルケースの文字列とアンダースコア区切りの文字列を相互変換してください。
追加された行頭・行末のアンダースコアはそのまま保持するものとし、
キャメルケースの文字列でもアンダースコア区切りの文字列でもない場合は
与えられた文字列をそのまま出力するものとします。
また、キャメルケースの文字列とアンダースコア区切りの文字列の両方に解釈できる文字列は
どちらであると考えても相互変換した結果が等しくなることが保証されます。

入力

入力は以下の形式で標準入力から与えられる。
c1c2…cN
入力として文字列が 1 行で与えられる。
入力の文字列長 N は、 1≤N≤50 を満たす。
i 文字目の文字 ci は 大文字のアルファベット (A, …, Z) 、
小文字のアルファベット (a, …, z) 、 数字 (0, …, 9) 、 アンダースコア (_ ) のいずれかである。

出力

入力として与えられた文字列を相互変換した文字列を標準出力に 1 行で出力せよ。
なお、行の終端には改行が必要である。

出典

B: camel_case - 天下一プログラマーコンテスト2012 予選B | AtCoder

回答

preg_replaceのe修飾子版
Submission #41990 - 天下一プログラマーコンテスト2012 予選B | AtCoder
AtCoder/tenka1_2012_B_2_preg_replace.php at master · wada811/AtCoder


preg_replace_callback版
Submission #41989 - 天下一プログラマーコンテスト2012 予選B | AtCoder
AtCoder/tenka1_2012_B_2_preg_replace_callback.php at master · wada811/AtCoder · GitHub

AtCoder上の回答を載せているのは通らなかったから…。
<?php
$variable = trim(fgets(STDIN));
if(isCamelCase($variable)){
    $variable = toSnakeCase($variable);
}else if(isSnakeCase($variable)){
    $variable = toCamelCase($variable);
}
echo $variable.PHP_EOL;
 
function isCamelCase($str){
    return preg_match('/^_*[a-z][a-z0-9]+([A-Z][a-z0-9]+)+_*$/', $str, $matches);
}
function toCamelCase($str){
    return preg_replace('/([a-z0-9])_([a-z])/e', '$1.strtoupper("$2")', $str);
}
function isSnakeCase($str){
    return preg_match('/^_*[a-z][a-z0-9]+(_[a-z][a-z0-9]+)+_*$/', $str, $matches);
}
function toSnakeCase($str){
    return preg_replace('/([a-z0-9])([A-Z])/e', '$1."_".strtolower("$2")', $str);
}
?>
preg_replaceだけで使えるe修飾子を使えば第二引数をPHPコードとして評価してくれます。
セキュリティ的にヤバイらしいので下のpreg_replace_callbackを使えと言われています。
<?php
$variable = trim(fgets(STDIN));
if(isCamelCase($variable)){
    $variable = toSnakeCase($variable);
}else if(isSnakeCase($variable)){
    $variable = toCamelCase($variable);
}
echo $variable.PHP_EOL;
 
function isCamelCase($str){
    return preg_match('/^_*[a-z][a-z0-9]+([A-Z][a-z0-9]+)+_*$/', $str, $matches);
}
function toCamelCase($str){
    return preg_replace_callback(
        '/([a-z0-9])_([a-z])/',
        create_function('$matches', 'return $matches[1].strtoupper($matches[2]);'),
        $str);
}
function isSnakeCase($str){
    return preg_match('/^_*[a-z][a-z0-9]+(_[a-z][a-z0-9]+)+_*$/', $str, $matches);
}
function toSnakeCase($str){
    return preg_replace_callback(
        '/([a-z0-9])([A-Z])/', 
        create_function('$matches', 'return $matches[1]."_".strtolower($matches[2]);'),
        $str);
}
?>
こちらは第二引数がコールバック関数になっているのでcreate_functionで作ってやります。
中でstrtoupperしたり、strtolowerしたりしてキャメルケースとスネークケースを相互変換してます。

綺麗なソースだろ?通らないんだぜ…これ…

どうやら正規表現の判定がミスっているらしいのだが…。
連結した後、最初や最後に1つ以上のアンダースコアが追加されることがあります。
最初と最後のアンダースコアをマッチ: /^_*(hoge)_*$/
単語
英小文字で始まる、英小文字・数字から成る文字列
単語: /[a-z][a-z0-9]+/
キャメルケースの文字列
1つ以上の単語を、最初の単語以外の先頭の文字を大文字にして連結した文字列
キャメルケース: /[a-z][a-z0-9]+([A-Z][a-z0-9]+)+/
最終的なキャメルケースの正規表現: /^_*[a-z][a-z0-9]+([A-Z][a-z0-9]+)+_*$/
ということでこれはよさそう。
アンダースコア区切りの文字列
1つ以上の単語を、単語の間を1つのアンダースコアで区切って連結した文字列
スネークケース: /[a-z][a-z0-9]+(_[a-z][a-z0-9]+)+/
単語をアンダースコア区切りで繰り返すのでこれで良いはず。
最終的なスネークケースの正規表現: /^_*[a-z][a-z0-9]+(_[a-z][a-z0-9]+)+_*$/
これも良さそうなのだが何やらAtCoderの結果を何度か見ていると
スネークケースが漏れているんじゃないかと思うようなことがあった。勘だけど…。

わからないのでわかった方ブログのコメントとかTwitterとかで教えてください。
お願いします。

タグ(RSS)