1246: 背包问题

1246: 背包问题

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 22   Solved: 6
[ Submit][ Status][ Discuss]

Description

有N个物品,每个物品有一定的重量,有M个袋子,它们有一定的承重,现在问使用这M个袋子,最多可以带走多少个物品.

Input

第一行一个数N 以下N行每行一个数,为每个物品的重量Ai 接下来一个数字M 以下M行每行一个数,为每个袋子的承重Bi

Output

仅一个数,为最多可以带走多少物品.

Sample Input

3
22
16
2
7
8
4
2
10
8
9
6

Sample Output

6

HINT

100%数据N<=5000 M<=300 Ai<=128 60%数据N<=100 M<=10 Ai<=100

Source

[ Submit][ Status][ Discuss]

看到题目时一点想法都没有
即使是被告知正解就是爆搜。。。还是。。。
首先我们尝试二分ans
因为很显然,,如果我们能装下k个,那么k+1也是合法的
二分了k,当然是要找最优的k个物品
所以,,如果我们能装下最轻的那k个,那问题也就有答案了
那谁该用谁来装呢?
首先,我们必须把k个物品全部装下
所以,不妨从最重的开始考虑,袋子也从最大的开始,
因为能装下最重的东西的袋子毕竟更少一些
对于同一个物品,如果第i个袋子和第j个袋子质量一样,那么谁去装它是等价的,枚举一次就行
因此,对物品排序&&对袋子排序
枚举的时候保证单调性,能大大提高效率
最后,还有一个可行性的判定
袋子能装的总质量tot是固定不变的
假设当前k个物品总重为w
那么允许浪费的空间最多就是tot - w
当一个袋子装了一些物品,导致剩下的空间连最轻的物品都装不下,那么剩下的空间就全部浪费了

以上剪枝全部加入程序,就能神奇地A掉这题啦。。。
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 5E3 + 50;

int n,m,tot,c[maxn],w[maxn],sum[maxn];
bool bo[maxn];

bool DFS(int pos,int waste,int Max)
{
	if (waste > Max) return 0;
	if (!pos) return 1;
	for (int i = n; i >= 1; i--)
		if (c[i] >= w[pos] && c[i] != c[i-1]) {
			c[i] -= w[pos];
			int pay = 0;
			if (c[i] < w[1]) pay = c[i];
			c[i] -= pay;
			bool flag = DFS(pos-1,waste + pay,Max);
			c[i] += w[pos];
			c[i] += pay;
			if (flag) return 1;
		}
	return 0;
}

bool Judge(int now)
{
	if (sum[now] > tot || w[now] > c[n]) return 0;
	return DFS(now,0,tot - sum[now]);
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	cin >> n;
	for (int i = 1; i <= n; i++) 
		scanf("%d",&c[i]),tot += c[i];
	cin >> m;
	for (int i = 1; i <= m; i++)
		scanf("%d",&w[i]);
	sort(w + 1,w + m + 1);
	for (int i = 1; i <= n; i++)
		if (c[i] < w[1]) 
			tot -= c[i],bo[i] = 1;
	int N = 0;
	for (int i = 1; i <= n; i++)
		if (!bo[i]) c[++N] = c[i];
	sort(c + 1,c + N + 1); n = N;
	for (int i = 1; i <= m; i++)
		sum[i] = sum[i-1] + w[i];
	
	int L = 0,R = m;
	while (R - L > 1) {
		int mid = (L + R) >> 1;
		if (Judge(mid)) L = mid;
		else R = mid;
	}
	if (Judge(R)) cout << R;
	else cout << L;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值