POJ-2184: Cow Exhibition

keyword

動的計画法 C

問題概要

長さN(<100)の点列(x,y) in [-R..R]^2(R=10^3)が与えられる。x+yの部分和の最大値を求めよ。ただし、xのみの部分和とyのみの部分和が共に非負でなければならない。

解法

範囲が狭いのがとても怪しい。もしGreedyで上手くいくならRはもっと大きく設定するだろう。
というわけでDPで考える。別にそういう本筋でない考え方を抜きにしても、情報をたくさん捨てても困らないときにはDPが使えることは多い。
で、この問題をDPで解くにはdp[k番目の点まで見た][xの部分和] = (yの最大の部分和) とすれば良い。xの部分和の範囲は[-N*R..N*R]なので全部とっておける(実は下限はもう少し削れるけど、せいぜい定数倍の差)。ただしN*N*2*Rはさすがに大きいので、dpテーブルは使いまわしてメモリを省略する。時間計算量O(N^2*R)。空間計算量O(N*R)。

#define MAX_N 102
#define MAX_RANGE (100*2000+2)
#define INF (1<<29)

int dp[2][MAX_RANGE];
int N;
int S[MAX_N], F[MAX_N];

int max(int x, int y){ return x>y?x:y; }

int solve(){
    int ans=0, i, k, cur, next;
    for(i=0;i<MAX_RANGE;i++) dp[0][i] = -INF;
    dp[0][100000] = 0;
    for(k=0;k<N;k++){
        cur = k&1, next = 1-cur;
        for(i=0;i<MAX_RANGE;i++) dp[next][i] = -INF;
        for(i=0;i<MAX_RANGE;i++)if(dp[cur][i]!=-INF){
            dp[next][i] = max(dp[next][i], dp[cur][i]);
            dp[next][i+S[k]] = max(dp[next][i+S[k]], dp[cur][i]+F[k]);
        }
    }
    for(i=100000;i<MAX_RANGE;i++)if(dp[N&1][i]>=0){
        ans = max(ans, (i-100000)+dp[N&1][i]);
    }
    return ans;
}

main(){
    int i;
    scanf("%d",&N);
    for(i=0; i<N; i++)
        scanf("%d%d",S+i,F+i);
    printf("%d\n",solve());
}