結果
| 問題 |
No.1413 Dynamic Sushi
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2021-03-01 00:31:14 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 3,265 ms / 4,000 ms |
| コード長 | 3,939 bytes |
| コンパイル時間 | 8,983 ms |
| コンパイル使用メモリ | 252,964 KB |
| 最終ジャッジ日時 | 2025-01-19 08:51:09 |
|
ジャッジサーバーID (参考情報) |
judge3 / judge1 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | AC * 25 |
ソースコード
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace std;
using namespace atcoder;
// デバッグ表示
#define dump(x) cout << #x << ":" << (x) << endl;
// 型定義
typedef long long ll;
typedef pair<ll, ll> P;
// forループ
#define REP(i,n) for(ll i=0; i<(ll)(n); ++i)
// 定数宣言
const int INF = 1e9;
const int MOD = 1e9+7;
const ll LINF = 1e18;
// modint
using mint = modint1000000007;
// using mint = modint998244353;
// グラフ表現
using Graph = vector<vector<int>>;
// グラフの辺表現
using Edge = map<pair<int,int>,int>;
// n次元配列の初期化。第2引数の型のサイズごとに初期化していく。
template<typename A, size_t N, typename T>
void Fill(A (&array)[N], const T &val){
std::fill( (T*)array, (T*)(array+N), val );
}
// コンビネーションを計算する関数
ll pow(ll N, ll k) {
ll res = 1;
for (ll i = 0; i < k; ++i) res *= N;
return res;
}
// 最大公約数
ll gcd(ll a,ll b){
if (a%b == 0) return(b);
else return(gcd(b, a%b));
}
// 最小公倍数
ll lcm(ll a, ll b){
return a/gcd(a, b) * b;
}
ll N, W;
double dp[1 << 13][13];
vector<ll> X;
vector<ll> Y;
vector<ll> R;
vector<ll> V;
vector<ll> A;
// 時間内に到達できるかの判定
double check(double posix, double posiy, double t, double m, ll j){
// 時刻timeのj番の頂点の位置
double nextx = X[j] + R[j] * cos((V[j]*(t+m)+A[j]) * M_PI / 180.0);
double nexty = Y[j] + R[j] * sin((V[j]*(t+m)+A[j]) * M_PI / 180.0);
double dis = sqrt((nextx-posix) * (nextx-posix) + (nexty-posiy) * (nexty-posiy));
if(m*W >= dis) return true;
else return false;
}
// 時刻tにi番目の頂点にいるときにi→jへ移動するときの所要時間
double calc(double t, ll i, ll j){
// 時刻tのi番の頂点の位置
double posix = X[i] + R[i] * cos((V[i]*t+A[i]) * M_PI / 180.0);
double posiy = Y[i] + R[i] * sin((V[i]*t+A[i]) * M_PI / 180.0);
double left = 0;
double right = 1e5;
REP(k, 50){
double middle = ((right - left) / 2) + left;
//大丈夫なときはもう少し小さい値を見る
if(check(posix, posiy, t, middle, j)){ // 範囲右寄せ,左寄せ条件
right = middle; // 左寄せ手法
} else {
left = middle; // 右寄せ手法
}
}
return right;
}
int main()
{
cout << fixed << setprecision(15);
cin >> N >> W;
X.resize(N+1);
Y.resize(N+1);
R.resize(N+1);
V.resize(N+1);
A.resize(N+1);
X[0] = 0;
Y[0] = 0;
R[0] = 0;
V[0] = 0;
A[0] = 0;
for(ll i=1; i<=N; i++){
cin >> X[i] >> Y[i] >> R[i] >> V[i] >> A[i];
}
for(ll S=0; S<(1<<(N+1)); S++){
for(ll v=0; v<(N+1); v++){
dp[S][v] = LINF;
}
}
// 0番目の点をスタートとする
dp[0][0] = 0;
dp[1][0] = 0;
for(ll S=0; S<(1<<(N+1)); S++){
for(ll v=0; v<(N+1); v++){
// 既にSに含まれる点の中の一つを対象に
if(S & (1<<v)){
for(ll j=1; j<N+1; j++){
// もう1つの点はSに含まれていないものなので、Sに含まれているものは飛ばす
if(S & (1<<j)) continue;
// Sに新たに点jを追加した時、そのときの最小ハミルトンパスはSの最小にdist[v][j]をつけたものになる
dp[S|(1<<j)][j] = min(dp[S|(1<<j)][j] , dp[S][v] + calc(dp[S][v], v, j));
// dump(S);
// dump(v);
// dump(calc(dp[S][v], v, j))
// cout << S << " " << v << " " << dp[S][v] << endl;
}
}
}
}
double ans = LINF;
for(ll i=1; i<=N; i++){
ans = min(ans, dp[(1<<(N+1))-1][i]);
// dump(ans);
}
cout << ans << endl;
return 0;
}