Hatena::Grouptopcoder

cafelier@SRM

cafelier のSRM参加記録です。コンテスト中に考えてたことを執拗に全部書き残すとどうなるだろうかという試み本番中にこういうコードが書きたかったなあ、という後で書いた反省コードを書き残す試み

スパムが来たのでしばらくコメント欄をはてなユーザ限定にしています、すみません、

|

2012-07-03

二分探索

23:09 | はてなブックマーク - 二分探索 - cafelier@SRM

みなさま二分探索ってどういう風に考えながらコードを書くのでしょうか、ということがちょっと気になりました。特に実数ではなくて整数のもの。問題:

private int X = 1 以上 9_9999_9999 以下の秘密の整数;
bool query(int x) { return X <= x; }

X を直接読まずに query() 関数を何回か呼び出すことで X を求めなさい。

自分の場合、こう考えながら書いています。

int solve()
{
  int L = 0;            // 条件を満たさないとわかっている数を適当に
  int R = 10_0000_0000; // 条件を満たすとわかっている数を適当に

  // 不変条件: 「常に答えは半開区間 (L, R] の範囲にある」

以下、意地でもこの条件を崩さないことだけを念頭においてコーディング。

以下、意地でもこの条件を崩さないことだけを念頭においてコーディング。(重要なので二回)

  while( R-L > 1 ) // 答えの範囲が整数一つになるまで繰り返し
  {
     int C = (L+R)/2; // 二分探索なので真ん中をとる
     (query(C) ? R : L) = C;
     // (query(C) ? L : R) = C; ではない。(L,R] が答えの範囲なので R が条件満たす方、L が満たさない方
  }
  return R; // 答えは (L,R] にあるのだから L じゃなくて R が答え
}

Topcoderにサブミットした二分探索のコード全部に半開区間の不変条件がコメントで書いてあります。これを念じていないと「ループ終了条件」「どっちの場合にLとRのどっちを更新するんだっけ?」「最後に返す値はどれ?」の3カ所で、USB端子の差し込み方向並に毎回迷ってしまうので。

チャレンジフェーズで他の人のコードを見ていると、違う考え方でコード書いている人が結構多いようなので興味が湧いています。if(query(C)) R=C; else L=C+1; みたいな形の更新式になるコードとか。気になります。

追記:反応集 http://togetter.com/li/331840

ほげほげ2012/07/04 00:51自分は while(L<R) で回して if(query(C)) R=C; else L=C+1; の更新式でした。
閉区間[L,R]で考えて、query(C) == false の時は C を閉区間から追い出して良いので次の範囲は[C+1,R]になる感じです。
ループ終了条件と返す値は分かり易いのですが、条件を満たす側が逆な場合等に丸め方向と更新式を変える必要があるのがネックでした。
半開区間の方が扱い易そうですね。

cafeliercafelier2012/07/04 02:21コメントありがとうございます。

> query(C) == false の時は C を閉区間から追い出し
こういう考え方を知りたかったのです!なるほど、閉区間で書くイメージもできてきました。

どのやり方も一長一短あるとは思うのですが、特にTopcoderで他人のコードを読むときに、自分のスタイルと違うのを見て詰まってしまう自分が勿体ないなあと思いまして。

cafeliercafelier2012/07/22 18:09「閉区間 & trueでもfalseでもmidを常に追い出す」という方針を教えていただいた。なるほど。
https://twitter.com/nabesan_tofu/status/226959241632690178
https://twitter.com/nabesan_tofu/status/226964459439136768

トラックバック - https://topcoder-g-hatena-ne-jp.jag-icpc.org/cafelier/20120703

2012-06-03

TCO12 R2C 500

04:32 | はてなブックマーク - TCO12 R2C 500 - cafelier@SRM

左下にある点の個数と右下にある点の個数を2パスで数えて3パス目で集計したけれど、1パスでできるなあと気づいて感心して書き直した物。

続きを読む

トラックバック - https://topcoder-g-hatena-ne-jp.jag-icpc.org/cafelier/20120603

2012-05-06

GCJ12 Round1B A

11:50 | はてなブックマーク - GCJ12 Round1B A - cafelier@SRM

Dで。普通。

続きを読む

GCJ12 Round1B B

11:50 | はてなブックマーク - GCJ12 Round1B B - cafelier@SRM

Dで。std.containerの使ったつもりになって書いたはいいがコンパイルエラーとれず使い方わからず、自分で書いた方が速え、という展開に…

続きを読む

GCJ12 Round1B C Small

11:50 | はてなブックマーク - GCJ12 Round1B C Small - cafelier@SRM

Dで。3進数マスクすぐ作れるのはいい感じ。Largeわからなかった。

続きを読む

トラックバック - https://topcoder-g-hatena-ne-jp.jag-icpc.org/cafelier/20120506

2012-04-22

TCO12 Round2A 450

04:04 | はてなブックマーク - TCO12 Round2A 450 - cafelier@SRM

300に泥はまりしすぎて時間かけられなかったせいで焦りすぎて変なtypoを直せず…終了直前にサンプル通せたコードがそのままpractice通ったので無念です。以下ちょっと整理したもの

続きを読む

トラックバック - https://topcoder-g-hatena-ne-jp.jag-icpc.org/cafelier/20120422

2012-04-18

競技プログラミングのためのD言語(4/2)

21:47 | はてなブックマーク - 競技プログラミングのためのD言語(4/2) - cafelier@SRM

言語というかライブラリによって意外と違うのが標準入出力の速度で、入力サイズ N の線形オーダ O(N) で解かないといけない類の問題では、注意しないとデータを読み込むだけでタイムアウトするのも日常茶飯事です。その辺の心配はないのか調べてみました(今までDでそういう問題解いたことがなかったのでした…)。以下、

  • 100万未満の自然数が一行に3個、100万行、のランダムデータの総和を計算
  • gcc 4.7.0 と dmd 2.059
  • Core i7 2.7GHz の Windows7PowerShell の Measure-Command コマンドで5回測定して中間値

をとった結果です(単位は秒)。最適化ありというのは g++ -O3 と dmd -O -release -inline で、なしというのは引数なし。

プログラム最適化あり最適化なし
(1) C++ iostream (手抜き)5.2245.428
(2) C++ iostream (真面目)1.1191.155
(3) C++ scanf0.4990.507
(4) D readln+split+to!int1.0781.529
(5) D readln+split+map!(to!int)1.0721.545
(6) D readf1.9582.335
(7) D scanf1.3931.410

なにかの優劣比較をしたいわけではないので、まったくひとかけらも厳密なベンチマークではありませんが、結論としては、まあ readln + split で問題ないのではないでしょうか、と思いました。というか、だいたいこんなオーダなのでOnline JudgeにD言語を入れる人は参考にしていただければという感じです。あと iostream が遅くてコンテストで困るという人はまずは tie(0) と sync_with_stdio(false) を (定期発言)。

// (1) C++ iostream (手抜き)
#include <iostream>
using namespace std;

int main() {
	long long total = 0;
	for(int a,b,c; cin>>a>>b>>c; )
		total += a+b+c;
	cout << total << endl;
}
// (2) C++ iostream (真面目)
#include <iostream>
using namespace std;

int main() {
	cin.tie(0);
	ios_base::sync_with_stdio(false);

	long long total = 0;
	for(int a,b,c; cin>>a>>b>>c; )
		total += a+b+c;
	cout << total << endl;
}
// (3) C++ scanf
#include <cstdio>
using namespace std;

int main() {
	long long total = 0;
	for(int a,b,c; scanf("%d%d%d",&a,&b,&c)==3; )
		total += a+b+c;
	printf("%lld\n", total);
}
// (4) D readln + split + to!int
import std.array;
import std.conv;
import std.stdio;

void main() {
	long total = 0;
	for(string s; (s=readln()).length; ) {
		auto ss = s.split();
		int a = ss[0].to!int();
		int b = ss[1].to!int();
		int c = ss[2].to!int();
		total += a+b+c;
	}
	writeln(total);
}
// (5) D readln + split + map!(to!int)
import std.algorithm;
import std.array;
import std.conv;
import std.stdio;

void main() {
	long total = 0;
	for(string s; (s=readln()).length; ) {
		auto ss = s.split().map!(to!int);
		total += ss[0]+ss[1]+ss[2];
	}
	writeln(total);
}
// (6) D readf
import std.array;
import std.conv;
import std.stdio;

void main() {
	long total = 0;
	for(int a,b,c; readf("%d %d %d\n",&a,&b,&c); )
		total += a+b+c;
	writeln(total);
}
// (7) D scanf
import std.c.stdio;

void main() {
	long total = 0;
	for(int a,b,c; scanf("%d%d%d\n",&a,&b,&c)==3; )
		total += a+b+c;
	printf("%lld\n", total);
}
トラックバック - https://topcoder-g-hatena-ne-jp.jag-icpc.org/cafelier/20120418
|

presented by cafelier/k.inaba under CC0