0w1

ABC 32 D - ナップサック問題 ( 01Knapsack, 3 solutions )

D: ナップサック問題 - AtCoder Beginner Contest 032 | AtCoder
いい練習になった。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;

int n, c;
int v[ MAXN ], w[ MAXN ];

void solve1(){ // n <= 30
    vector<pll> set1;
    int s1 = n / 2;
    for(int i = 0; i < ( 1 << s1 ); ++i){
        ll vsum = 0LL, wsum = 0LL;
        for(int j = 0; j < s1; ++j){
            if( ( i >> j ) & 1 )
                vsum += v[ j ],
                wsum += w[ j ];
        }
        set1.push_back( pll( wsum, vsum ) );
    }
    sort( set1.begin(), set1.end() );
    set1.erase( unique( set1.begin(), set1.end() ), set1.end() );
    
    int vi = 1;
    for(int i = 1; i < set1.size(); ++i)
        if( set1[ i - 1 ].second < set1[ i ].second )
            set1[ vi++ ] = set1[ i ];
    set1.erase( set1.begin() + vi, set1.end() );
    
    ll ans = 0LL;
    int s2 = n - s1;
    for(int i = 0; i < ( 1 << s2 ); ++i){
        ll vsum = 0LL, wsum = 0LL;
        for(int j = 0; j < s2; ++j){
            if( ( i >> j ) & 1 )
                vsum += v[ s1 + j ],
                wsum += w[ s1 + j ];
        }
        int idx = upper_bound( set1.begin(), set1.end(), pll( c - wsum, (ll)( 1e16 ) ) ) - set1.begin();
        if( --idx < 0 ) continue;
        ans = max<ll>( ans, set1[ idx ].second + vsum );
    }
    printf("%lld\n", ans);
}

void solve2(){ // w <= 1000
    vector<ll> dp( c + 1 ); // maximum value when weight is i
    fill( dp.begin(), dp.end(), (ll)( -1e16 ) );
    dp[ 0 ] = 0LL;
    for(int i = 0; i < n; ++i)
        for(int j = c - w[ i ]; j >= 0; --j)
            dp[ j + w[ i ] ] = max<ll>( dp[ j + w[ i ] ], dp[ j ] + v[ i ] );
    printf("%lld\n", *max_element( dp.begin(), dp.end() ));
}

void solve3(){ // v <= 1000
    int mv = *max_element( v, v + n ) * n;
    vector<ll> dp( mv + 1 ); // minimum weight when value is i
    fill( dp.begin(), dp.end(), (ll)( 1e16 ) );
    dp[ 0 ] = 0LL;
    for(int i = 0; i < n; ++i)
        for(int j = mv - v[ i ]; j >= 0; --j)
            dp[ j + v[ i ] ] = min<ll>( dp[ j + v[ i ] ], dp[ j ] + w[ i ] );
    for(int i = mv; i >= 0; --i)
        if( dp[ i ] <= c ){
            printf("%d\n", i);
            return;
        }
}

void solve(){
    if( n <= 30 ) solve1();
    else if( *max_element( w, w + n ) <= 1000 ) solve2();
    else solve3();
}

int main(){
    scanf("%d%d", &n, &c);
    for(int i = 0; i < n; ++i)
        scanf("%d%d", &v[ i ], &w[ i ]);
    solve();
    return 0;
}