AOJ 0042: A Thief

A Thief
コード

ただのナップサック問題。だったので、紙を使わずに暗算で漸化式を立てて解きました。

dp[j][i] = i個目までの美術品をjの重さで盗んだ時の価値の最大値
初期状態は、dp[j][0] = 0 for all j
漸化式は、dp[j][i] = max(dp[j][i], dp[j-w][i]))で更新。

#include <iostream>
#include <algorithm>
#include <stdio.h>
 
using namespace std;
 
#define WMAX 1000
#define NMAX 1000
int main(void)
{
    int W, n;
    int cyc = 0;
    while (cin >> W && W && cin >> n) {
        cyc++;
        int dp[WMAX+1][NMAX+1] = {};
        for (int i = 0; i < n; i++) {
            int v, w;
            scanf("%d,%d", &v, &w);
            for (int j = 0; j < W+1; j++) 
                dp[j][i+1] = max(dp[j][i], (j-w >= 0 ? dp[j-w][i] + v : 0));
        }
        cout << "Case " << cyc << ":" << endl;
        for (int j = 0; j < W+1; j++) {
            if (dp[j][n] == dp[W][n]) {
                cout << dp[j][n] << endl << j << endl;
                break;
            }
        }
    }
}

AOJ 2301: Sleeping Time

Sleeping Time
コード

難易度Blue 350 (非公式難易度表)

「k回の睡眠でR=r, L=lとした時のPP」を返す関数retを考えて、深さ優先探索を行う発想は自然。
だが、これだけでは2^30で計算が間に合わない。

なので、l, rとP, Eの関係に枝刈り条件を加える。
ret内のl-rを呼び出し回数に関して指数関数的に収束させていくので、この条件はかなり強い枝刈りが可能である。

#include <iostream>
#include <cstdio>
using namespace std;
#define abs(a) ((a) > 0 ? (a) : -(a))
 
double P, E, T;
double ret(double k, double r, double l)
{
    double h = (r + l) / 2;
    if (k == 0) 
        return (abs(h-T) < E ? 1.0 : 0.0);
    if (l < T - E)
        return 0.0;
    if (r > T + E) 
        return 0.0;
    if (T - E < r && l < T + E) 
        return 1.0;
    if ((r+l)/2 >= T) 
        return P * ret(k-1, r, h) + (1.0 - P) * ret(k-1, h, l);
    else
        return P * ret(k-1, h, l) + (1.0 - P) * ret(k-1, r, h);
}
 
int main(void)
{
    double K, R, L;
    cin >> K >> R >> L >> P >> E >> T;
    P = 1 - P;
 
    printf("%.6f", ret(K, R, L));
    return 0;
}

AOJ 2015: Square Route

Square Route
コード

縦横で生成しうる辺の長さをそれぞれ全て求める。
それをソートしてしゃくとり法によって同じ長さの辺をかけて足し算していく。
でも、mapを使ってベクトルの内積をすればいいだけでは…と後で気づいた。

#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;vector&gt;

using namespace std;
#define NUMMAX 1500
#define CMAX (NUMMAX * (NUMMAX + 1) / 2)

void makecvec(vector&lt;int&gt;&amp; in, vector&lt;int&gt;&amp; out) 
{
    for (int ni = 0; ni &lt; (int)in.size(); ni++) {
        for (int pi = 0; pi &lt; (int)in.size() - ni; pi++) {
            int sum = 0;
            for (int si = 0; si &lt; ni + 1; si++)
                sum += in[pi + si];
            out.push_back(sum);
        }
    }
}

int main(void)
{
    int n, m;
    while (cin &gt;&gt; n &gt;&gt; m) {
        if (!n &amp;&amp; !m) break;
        vector&lt;int&gt; h(n), w(m);
        for (int i = 0; i &lt; n; i++) cin &gt;&gt; h[i];
        for (int i = 0; i &lt; m; i++) cin &gt;&gt; w[i];

        vector&lt;int&gt; hs, ws;
        hs.reserve(CMAX), ws.reserve(CMAX);
        makecvec(h, hs), makecvec(w, ws);
        sort(hs.begin(), hs.end()), sort(ws.begin(), ws.end());

        int result = 0;
        int hi = 0, wi = 0;
        while (hi != (int)hs.size() &amp;&amp; wi != (int)ws.size()) {
            if (hs[hi] == ws[wi]) {
                int hn = 0, wn = 0;
                while (hi != (int)hs.size() &amp;&amp; hs[hi] == hs[hi-hn]) 
                    hi++, hn++;
                while (wi != (int)ws.size() &amp;&amp; ws[wi] == ws[wi-wn]) 
                    wi++, wn++;
                result += wn * hn;
            } else if (hs[hi] &lt; ws[wi]) 
                hi++;
            else 
                wi++;
        }
        cout &lt;&lt; result &lt;&lt; endl;
    }

    return 0;
}

AOJ 0557: 1年生 (A First Grader)

1年生 (A First Grader)
コード

DP強化週間

num[j] = j項目の数字
a[i][j] = j個目までの数字を使ってAOJ君が計算可能な数式によって、iを作る時の場合の数

とすると、以下のような漸化式が立てられる。

a[j][i] = a[j + num[i]][i-1] (もしj + num[i] < 21なら) + a[j - num[i]][i-1] (もしj - num[i] >= 0)

i番目までで作れる0から20の数字を記憶する。何を展開して漸化式を立てるか、という問題は、もともと少ない自由度をわざわざ大きくする、という点で発想的だよなー。

#include <iostream>
#include <vector>

using namespace std;

int main(void)
{
    int n;
    cin >> n;
    int num[101];
    for (int i = 0; i < n-1; i++) 
        cin >> num[i];
    int dest;
    cin >> dest;

    long long a[21][101] = {};
    a[num[0]][0] = 1;
    for (int i = 1; i < n-1; i++) 
        for (int j = 0; j < 21; j++) 
            a[j][i] += ((j + num[i] < 21) ? a[j + num[i]][i-1] : 0) + ((j - num[i] >= 0) ? a[j - num[i]][i-1] : 0);
    cout << a[dest][n-1-1] << endl;

    return 0;
}

AOJ 0168: Kannondou

Kannondou
コード

DP強化週間

「漸化式を立てる」という発想があるだけで勝ち。
1, 2, 3段までは初期値として手で数え、それ以降は-1, -2, -3段の場合の数の足しあわせとして漸化的に計算する。

ちなみに初期の3段目を間違えて3通りだと思って、サンプルが通らなかったので、なるべく自動に任せた方が良いなと教訓を得ました。

#include <iostream>
using namespace std;
 
int main(void)
{
    int n;
    long long a[30] = {1, 2, 4};
    for (int i = 3; i < 30; i++) 
        a[i] = a[i - 1] + a[i - 2] + a[i - 3];
    while (cin >> n && n) 
        cout << 1 + a[n-1] / 3650 << endl;
 
    return 0;
}

1, 2, 3段の数え間違いを避けたいなら、負の段の場合の数を0, 0段目を1, 1段目を0として、番兵法とかを使うと良いかもしれない。

Syntax Hightlighter Evolved Installation

Syntax Hightlighter Evolvedを2時間くらいかけてインストールした。

以下、紆余曲折。
幻想的な導入に従うが、例の如く一発で動くわけがない。
・pluginを入れるためのftpdが入っていなかった。deamonの立ち上げ方がわからなかったため時間を食った。chkconfig, serviceなるものを使った。
・pamを使った認証は設定が面倒らしいし、みんな逃げていたので僕もpamから逃げた。
・phpのzlib extensionが入っていなかった。phpをconfigureし直して、installした。面倒くさい。
・埋め込んだつもりのコードが綺麗に表示されない。http://www.imaginationdesign.jp/blog/wordpress/1733/のページを参考に、AKB48みたいな名前をした以下の2つのフォルダに改変を施す。wp-content/themes/twentyten/, wp-content/themes/twentyeleven/
・その後も10分、正しくpluginが動かなかったが、コーヒーを飲んで返ってきたら動いていた。

やはり、情報はやる気を削ぐ天才だと思う。