366C
题目链接:http://codeforces.com/problemset/problem/366/C
题目可以表达为:n件物品,每个物品有两个属性a和b,从中选出若干件使得sum{a[i]}/sum{b[i]}=k,且sum{a[i]}最大。
把a[i]看作第i件物品的价值,a[i]-k*b[i]看成是第i件物品的容量,就是01背包。
如果把正容量和负容量的物品分成两组,将负容量的取反,分别进行01背包(恰好装满),那么相同大小的背包就能相互抵消。
有的大神直接用一个背包完成:用一个正的偏移量来作为0大小的背包,遇到负容量的物品顺序循环,遇到正容量的物品逆序循环。
# include <stdio.h>
# include <string.h>
int n, k;
int a[105], b[105];
int ca[105], va[105], na;
int cb[105], vb[105], nb;
int fa[105*105], fb[105*105];
int ans;
const int INF = 1000000000;
const int Cmax = 105*105-1;
int Select(int x, int y) {
int r = x<y ? y:x;
return r < 0 ? -INF:r;
}
void pack(int f[], int nn, int c[], int v[])
{
for (int j = Cmax; j >= 0; --j) f[j] = -INF;
f[0] = 0;
for (int i = 0; i < nn; ++i) {
for (int j = Cmax; j >= c[i]; --j) {
f[j] = Select(f[j], f[j-c[i]]+v[i]);
}
}
}
int main()
{
ans = 0;
na = nb = 0;
int Cmax = 105*105-1;
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i) scanf("%d", &b[i]);
for (int i = 1; i <= n; ++i) {
int t = a[i] - k*b[i];
if (t < 0) {
ca[na] = -t;
va[na++] = a[i];
} else if (t > 0) {
cb[nb] = t;
vb[nb++] = a[i];
} else {
ans += a[i];
}
}
pack(fa, na, ca, va);
pack(fb, nb, cb, vb);
int tmax = 0;
for (int j = Cmax;j >= 0; --j) {
if (fa[j]>=0 && fb[j]>=0) {
if (tmax < fa[j]+fb[j]) tmax = fa[j]+fb[j];
}
}
printf("%d\n", tmax+ans<=0 ? -1:tmax+ans);
return 0;
}

浙公网安备 33010602011771号