C. Dima and Salad 背包好题

http://codeforces.com/contest/366/problem/C

在n个物品中选出若干个,使得sum(a[i]) = k * sum(b[i])

把问题转化一下就是,求sum(a[i] - k * b[i]) = 0的最大的a[i],这个时候已经把a[i]作为价值了

那么怎么去求呢?

一开始因为a[i] - k * b[i]有负数,那么需要fix值,fix = 1000

我只设了dp[v]表示产生这个和值时的最大价值。那么如果能产生这个v值,就需要这个v值能整除fix。因为sigma(a[i] - k * b[i])=0

那么这个v值其实就是若干个fix值相加而已。比如-1,fix后是999。。1,fix后是1001,相加是2000

所以只有v % fix == 0的才能作为贡献。但是有bug。因为你不知道它是多少个数相加得到的v。如果是5个数得到的v,那么需要这个v要整除5 * fix才可以,所以我就用了dp[i][j]表示选了i个数产生j的最大价值。但是这样转移是1e9的,但是水过去了。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;


#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
const int maxn = 1e2 + 20;
int a[maxn], b[maxn];
struct node {
    int w, val;
}c[maxn];
const int fix = 1000;
int dp[100 + 2][1099 * 100 + 20];
void work() {
    int n, k;
    cin >> n >> k;
    int tot = 0;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 1; i <= n; ++i) {
        cin >> b[i];
        c[i].w = a[i] - k * b[i] + fix;
        c[i].val = a[i];
        tot += c[i].w;
    }
    memset(dp, -0x3f, sizeof dp);
//    cout << -inf << endl;
    cout << dp[0][0] << endl;
    dp[0][0] = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = i; j >= 1; --j) {
            for (int k = tot; k >= c[i].w; --k) {
                dp[j][k] = max(dp[j][k], dp[j - 1][k - c[i].w] + c[i].val);
            }
        }
    }
    int ans = -1;
    for (int i = 1; i <= n; ++i) {
        for (int j = i * fix; j <= tot; j += fix) {
            ans = max(ans, dp[i][j]);
        }
    }
    cout << ans << endl;
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    IOS;
    work();
    return 0;
}
View Code

所以这个是二维费用背包问题。

 

5 9
100 100 100 100 100
100 100 100 100 100

 

 

然后这题的正解因该是,分开dp,就是一样的转移问题是a[i] - k * b[i]

然后这些东西有正有负,那么就分开吧,dpup[v]表示整数那堆数,产生v这个数字的最大价值。

那么需要dpup[v] + dpdown[v]

注意有0的情况,0要单独处理一下,因为可能dpup[0]有值,但是dpdown[0]是-inf。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;


#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
const int maxn = 1e2 + 20;
int a[maxn];
int b[maxn];
int c[maxn];
int dpup[100 * 1000 + 20];
int dpdown[100 * 1000 + 20];
void work() {
    int n, k;
    cin >> n >> k;
    int tans = 0;
    int tot = 100 * 1000;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; ++i) {
        cin >> b[i];
        c[i] = a[i] - k * b[i];
        if (c[i] == 0) {
            tans += a[i];
        }
    }
    memset(dpup, -0x3f, sizeof dpup);
    memset(dpdown, -0x3f, sizeof dpdown);
//    cout << dpup[1] + dpdown[1] << endl;
    dpup[0] = dpdown[0] = 0;
    for (int i = 1; i <= n; ++i) {
//        if (c[i] == 0) continue;
        if (c[i] > 0) {
            for (int j = tot; j >= c[i]; --j) {
                dpup[j] = max(dpup[j], dpup[j - c[i]] + a[i]);
            }
        } else {
            c[i] = -c[i];
            for (int j = tot; j >= c[i]; --j) {
                dpdown[j] = max(dpdown[j], dpdown[j - c[i]] + a[i]);
            }
        }
    }
    int ans = -1;
    for (int i = 1; i <= tot; ++i) {
        ans = max(ans, dpup[i] + dpdown[i]);
    }
    if (tans != 0) ans = max(tans, ans);
    cout << ans << endl;
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    IOS;
    work();
    return 0;
}
View Code

 

posted on 2016-12-30 16:49  stupid_one  阅读(286)  评论(0编辑  收藏  举报

导航