NoiminのNoise

競技プログラミング (多め) とWeb (たまに) ,自然言語処理 (ブログではまだ)。数式の書き方を一気に KaTeX に変えようとして記事を全削除してインポートし直すなどしたので,過去にブックマークされた記事は URL が変わってしまっている可能性があります…….

JAG 夏合宿 2010 3日目 B - Carrot Tour

Carrot Tour | Aizu Online Judge

問題概要

合計移動距離がr以下(0 < r < $ 10^4$)になるように20個以下の都市を回る. 都市を訪れる回数を最大化せよ. ただし,進む向きを一度に$ \theta$度より大きく変えることはできない.

解法概要

都市が20個以下という制約に反応してbitDPを書きたくなるが,これはbitDPでないDP (見事に引っかかってしまった) .

dp[訪れた都市の数][1個前の都市][今の都市]の配列を埋めていけば良い.訪れることのできる都市の数はたかだか$ 10^4$個なので,都市の数をnとして(都市だけに……) $ O( \lfloor r \rfloor n^3)$のDPを書いても間に合う.都市間の距離や,曲がることができるかどうかは事前計算しておけば良い.

配列の初期化ミスに注意 (私は配列の初期化で何重にもミスを重ね,この問題に半日かけた) .

ソースコード

#include<iostream>
#include<utility>
#include<cmath>
#include<algorithm>
#include<climits>
#include<complex>

#define debug(x) cerr << #x << ": " << x << endl
#define EPS 1e-5
#define MAX_R 200000.0
 
using namespace std;

int main(){
    int n;
    double r, theta;
    cin >> n;
    cin >> r >> theta;
    theta = theta/180.0*M_PI;
    complex<double> cities[n];
    for(int i=0;i<n;i++){
        double x,y;
        cin >> x >> y;
        cities[i] = complex<double>(x, y);
    }
 
    bool ok[n][n][n];
    fill_n(**ok, n*n*n, false);
    double dist[n][n];
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(i == j) continue;
            complex<double> v = cities[j] - cities[i];
            dist[i][j] = abs(v);
            for(int k=0;k<n;k++){
                if(i == k || j == k) continue;
                complex<double> u = cities[k] - cities[j];
                double theta_ = abs(arg(u/v));
                // theta_ = min(abs(arg(u) - arg(v)), 2*M_PI-abs(arg(u) - arg(v)))でも同じ
                if(theta_ < theta+EPS){
                    ok[i][j][k] = true;
                }
            }
        }
    }

    double dp[2][n][n];
    fill(dp[0][0], dp[0][n], MAX_R);
    for(int j=1;j<n;j++){
        dp[0][0][j] = dist[0][j];
    }
    int i = 0, ans = 0;
    for(int i=0;i<=10000;i++){
        fill(dp[1-(i&1)][0], dp[1-(i&1)][n], MAX_R);
        for(int j=0;j<n;j++){
            for(int k=0;k<n;k++){
                if(j == k || dp[i&1][j][k] > r+EPS) continue;
                for(int l=0;l<n;l++){
                    if(j == l || k == l) continue;
                    if(ok[j][k][l]){
                        dp[1-(i&1)][k][l] = min(dp[1-(i&1)][k][l], dp[i&1][j][k] + dist[k][l]);
                    } 
                }
                ans = i+1;
            }
        }
    }
 
    cout << ans << endl;
}

所感

達成感はあるがとっても疲れた. 明日提出のレポートどうするの (知るか)