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;
}

 

posted @ 2013-11-25 09:42  努力变瘦  阅读(403)  评论(0)    收藏  举报