SRM468 Div2 Hard(1000) Gifts
贈り物・王・王妃の間の最短距離を予め求めておく。位置と持っている贈り物を状態として、動的計画法。ある地点xで贈り物を拾わない場合は、xを通らなかったとみなせる。
#include <string> #include <vector> using namespace std; class Gifts { int countbit( int n ); public: int maxGifts( vector <string> city, int T ); }; int Gifts::maxGifts( vector <string> city, int T ) { int h = (int)city.size(); int w = (int)city[0].size(); // 贈り物の個数を数え、贈り物・王・王妃に番号を振る // gn:贈り物の個数 // 王:gn、王妃:gn+1 int gn = 0; vector<vector<int> > index( h, vector<int>( w, -1 ) ); for ( int y=0; y<h; y++ ) for ( int x=0; x<w; x++ ) if ( city[y][x] == 'G' ) index[y][x] = gn++; for ( int y=0; y<h; y++ ) for ( int x=0; x<w; x++ ) { if ( city[y][x] == 'K' ) index[y][x] = gn; if ( city[y][x] == 'Q' ) index[y][x] = gn + 1; } // 贈り物・王・王妃の間の最小距離を求める vector<vector<int> > dist( gn+2, vector<int>( gn+2 ) ); for ( int fy=0; fy<h; fy++ ) for ( int fx=0; fx<w; fx++ ) if ( index[fy][fx] >= 0 ) { vector<vector<int> > map( h, vector<int>( w, T+1 ) ); map[fy][fx] = 0; bool up = true; for ( int d=0; up; d++ ) { up = false; for ( int y=0; y<h; y++ ) for ( int x=0; x<w; x++ ) if ( map[y][x] == d ) { for ( int dir=0; dir<4; dir++ ) { int tx = x + dir/2 * ( 1 - dir%2*2 ); int ty = y + (1-dir/2) * ( 1 - dir%2*2 ); if ( 0 <= tx && tx < w && 0 <= ty && ty < h && city[ty][tx] != '#' && map[ty][tx] == T + 1 ) { map[ty][tx] = d + 1; up = true; } } } } for ( int y=0; y<h; y++ ) for ( int x=0; x<w; x++ ) if ( index[y][x] >= 0 ) dist[ index[fy][fx] ][ index[y][x] ] = map[y][x]; } // 位置pに贈り物gを持って到達する最小時刻を求める // i番目のビットでi番目の贈り物を表す vector<vector<int> > time( gn+2, vector<int>(1<<gn,T+1) ); // [p][g] for ( int p=0; p<gn+2; p++ ) time[p][0] = dist[gn][p]; for ( int n=0; n<=gn; n++ ) { for ( int p=0; p<gn+2; p++ ) for ( int g=0; g<(1<<gn); g++ ) if ( countbit(g) == n && time[p][g] < T + 1 ) { for ( int q=0; q<gn+2; q++ ) if ( dist[p][q] <= T ) { int gt = p<gn ? g|1<<p : g; time[q][gt] = min( time[q][gt], time[p][g] + (countbit(gt)+1)*dist[p][q] ); } } } // 王妃の位置について、最小到達時刻がT以内で、 // 贈り物の個数が最大であるものが答え int ans = 0; for ( int g=0; g<(1<<gn); g++ ) if ( time[gn+1][g] <= T ) ans = max( ans, countbit(g) ); return ans; } int Gifts::countbit( int n ) { n = ( n & 0x55555555 ) + ( n >> 1 & 0x55555555 ); n = ( n & 0x33333333 ) + ( n >> 2 & 0x33333333 ); n = ( n & 0x0f0f0f0f ) + ( n >> 4 & 0x0f0f0f0f ); n = ( n & 0x00ff00ff ) + ( n >> 8 & 0x00ff00ff ); n = ( n & 0x0000ffff ) + ( n >>16 & 0x0000ffff ); return n; }