0w1

CFR 505 C. Mr. Kitayuta, the Treasure Hunter ( Memoization DP )

Problem - C - Codeforces
題意:
給一堆節點,表示節點上有寶石,重複輸入節點代表有多少寶石,和一開始跳躍的距離 D,代表初始時在 D 編號的島上,且最後一次跳躍的距離是 D。接著每次移動可以選擇移動最後一次移動的長度 -1 / 0 / +1,但不能不移動( 移動距離等於 0 )。求最多可以採集多少寶石。
解法:
考慮究竟有多少移動長度是會被用上的。試著每次都 +1 不斷疊上去,便會發現最多只會到約 +250。因此,實際上會用到的最後移動距離最多只有約 500 種( 因為還有負的要考慮 )。因此可以考慮動態規劃。
dp[ i ][ j ] : 目前在 i 號島,且最後一次移動距離為 j - D + 250,從這裡開始計算,未來可能的最大採集寶石數。
dp[ i ][ j ] = val[ i ] + max{ dp[ i + j - 1 ][ j - 1 ], dp[ i + j ][ j ], dp[ i + j + 1 ][ j + 1 ] }
狀態只需要開 O( N * sqrt( N ) ) = 30000 * 250。
於是我試著用 unordered_map,發現這東西還真慢。。。怎樣都不給過呢。

int N, D;
vi P;

void init(){
    cin >> N >> D;
    P = vi( N );
    for( int i = 0; i < N; ++i )
        cin >> P[ i ];
}

vi cnt;

/*
struct hashfunc {
    template<typename T, typename U>
    size_t operator()(const pair<T, U> &x) const {
        return hash<T>()(x.first) ^ hash<U>()(x.second);
    }
};

unordered_map< pii, int, hashfunc > dp;
*/

int dp[ 30001 ][ 500 + 5 ];

int dfs( int u, int l ){
    if( not ( 0 < l and u <= 30000 ) )
        return 0;
    if( ~dp[ u ][ l - D + 250 ] )
        return dp[ u ][ l - D + 250 ];
    return dp[ u ][ l - D + 250 ] = cnt[ u ] + max( { dfs( u + l - 1, l - 1 ), dfs( u + l, l ), dfs( u + l + 1, l + 1 ) } );
}

void preprocess(){
    cnt = vi( 30000 + 1 );
    for( int i = 0; i < N; ++i )
        ++cnt[ P[ i ] ];
    memset( dp, -1, sizeof( dp ) );
}

void solve(){
    cout << dfs( D, D ) << endl;
}