题解/算法 {E. Breakfast II}
@LINK: https://codeforces.com/gym/105184
;
首先 不要一看到涉及到几何 就以为就是几何算法, 不是的…
通过N,M, b,c
的值, 你可以得到一个Need
表示 所有人需要去食堂的次数;
每个人 预处理一个Cost[a][b]
表示a这个人 去b
个食堂 且到达办公室的 最小代价 (1<=b <= 3
), 如果b==0
表示 这个人待在原地;
.
比如b==2
, 那么就暴力枚举 他的所有路径可能 我们用0,1,2 表示这3个食堂; S为宿舍, T为办公室
, 则枚举全排列S -> {[01] [10] [02] [20] [12] [20]} -> T
;
然后就可以用DP了, 这些人 每个人有4种可能{0: 原地; 1: 去一次食堂; 2: 去两次; 3: 去三次}
, DP求一个DP[I][J]
表示[0...I]
这些人 他们去了J
次食堂的 最小代价;
int Need;
vector< pair<int,int> > Store(3);
pair<int,int> Target;
vector< pair<int,int> > Person;
int N;
{
int n, m; cin>> n>> m>> N;
int b, c; cin>> b>> c;
Need = std::max( (n+b-1)/b, (m+c-1)/c);
FOR_( i, 0, 2){
cin>> Store[i].first>> Store[i].second;
}
cin>> Target.first>> Target.second;
Person.resize( N);
for( auto & i : Person){
cin>> i.first>> i.second;
}
}
auto GetDist = []( std::pair<int,int> & _a, std::pair<int,int> & _b){
int dx = std::abs(_a.first-_b.first); dx *= dx;
int dy = std::abs(_a.second-_b.second); dy *= dy;
return std::sqrt( ___Double_( dx + dy));
};
static ___Double_ Cost[ 1003][ 4];
FOR_( i, 0, N-1){
Cost[i][0] = 0;
FOR_( j, 1, 3){
Cost[ i][ j] = 1e11;
}
}
FOR_( p, 0, N-1){
FOR_( st, 1, 0b111){
std::vector<int> permu;
FOR_( bit, 0, 2){
if( (st>> bit) & 1){ permu.emplace_back( bit);}
}
do{
___Double_ cost = 0;
std::pair<int,int> pre = Person[p];
for( auto m : permu){
cost += GetDist( pre, Store[m]);
pre = Store[m];
}
cost += GetDist( pre, Target);
{
auto & update = Cost[ p][ __builtin_popcount(st)];
update = std::min( update, cost);
}
}while( std::next_permutation( permu.begin(), permu.end()));
}
}
___Double_ DP[ 1003][ 1003];
FOR_( I, 0, N-1){
if( I == 0){
FOR_( J, 0, Need){ DP[I][J] = 1e11;}
FOR_( J, 0, 3){ DP[I][J] = Cost[I][J];}
continue;
}
std::memcpy( DP[I], DP[I-1], sizeof(DP[I]));
FOR_( J, 0, Need){
FOR_( cont, 1, 3){
auto const& preDP = DP[ I - 1][ J];
if( preDP > 1e10){ continue;}
if( J + cont > Need){ continue;}
DP[I][ J + cont] = std::min( DP[I][ J + cont], preDP + Cost[I][cont]);
}
}
}
if( DP[N-1][Need] > 1e10){
ASSERT_(0);
}
cout<< DP[N-1][Need];