The 17th Zhejiang Provincial Contest B. Bin Packing Problem(线段树+map)

Posted li_wen_zhuo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了The 17th Zhejiang Provincial Contest B. Bin Packing Problem(线段树+map)相关的知识,希望对你有一定的参考价值。

题目描述

Lzw is having the Operations Research class now. Today the teacher is talking about the bin packing problem and some approximation algorithms to solve it.
In the bin packing problem, items of different volumes must be packed into a finite number of bins with a fixed capacity C in a way that minimizes the number of bins used. In computational complexity theory, it is a combinatorial NP-hard problem.
There are two classical approximation algorithms.
First Fit Algorithm. Consider the items in input order and maintain a list of bins, initially empty. In each step, it attempts to place the current item in the first bin in the list that can accommodate the item. If no suitable bin is found, it appends a new bin at the end of the list and puts the item into the new bin.
Best Fit Algorithm. Consider the items in input order and maintain a list of bins, initially empty. In each step, it attempts to place the current item in the best bin that can accommodate the item. “Best” means that if there are more than one bins that can accommodate the item, the bin with the least remaining space is chosen. If no suitable bin is found, a new bin is added and the item is put into the new bin.
Please help Lzw implement these two algorithms and find out how many bins will each algorithm use.

Input

The input contains multiple cases. The first line of the input contains a single positive integer T, the number of cases.
For each case, the first line of the input contains two integers n,C (1≤n≤106, 1≤C≤109), the number of items and the capacity of each bin. The second line contains n integers, where the i-th (1≤i≤n) integer ai (1≤ai≤C) denotes the volume of the i-th item.
It is guaranteed that the sum of n over all cases doesn’t exceed 106.

Output

For each case, print a single line containing two integers, denoting the number of bins used by the First Fit and the Best Fit algorithm, respectively.

Example

input
2
2 2
1 1
5 10
5 8 2 5 9
output
1 1
4 3

题目大意

按顺序给一串序列,代表物品的重量,并且给定容器的容量大小, 要求最少需要几个容器存放这些物品。
题中给了两种算法:
从左到右遍历容器,找到第一个可以放的容器,把物品放进去,如果放不了,再开一个位置。
找到能放并且当前剩余容量最小的容器,如果放不下,再开一个位置。

题目分析

这两个算法的暴力都是O(n2),因此我们需要用数据结构进行优化。

第一种算法:从左到右遍历容器,找到第一个可以放下的容器放进去。即最左边剩余空间大于w[i]的容器。这里我们可以用线段树来维护区间最大值,线段树中存储每个容器的剩余空间。
查询操作是查询线段树中大于等于w[i]的最左边的位置。

因为线段树的大小是固定的,因此我们初始化时可以往线段树中加入n个空的容器(n个C)。用k来记录启用的容器个数,如果当某次查询的值大于k时,说明需要新的容器了,则让k+1。
最后的k即为算法1的答案。

第二种算法:找到能放当前物品且容积最小的容器放入。这个直接维护一个map/set集合即可。当放入物品时,用lower_bound()函数来查询集合中大于等于w[i]的值的即可(集合中也是存的当前容器的剩余容量)

代码如下
#include <iostream>
#include <cmath>
#include <cstdio>
#include <set>
#include <string>
#include <cstring>
#include <map>
#include <algorithm>
#include <stack>
#include <queue>
#include <bitset>
#define LL long long
#define ULL unsigned long long
#define PII pair<int,int>
#define PDD pair<double,double>
#define x first
#define y second
using namespace std;
const int N=2e6+5,INF=1e9+7;
int n,s,w[N],tr[N*4];
map<int,int> b;
void build(int u,int l,int r)		//线段树
{
    tr[u]=s;						//初始值全为s(C)
    if(l==r) return;
    int mid=l+r>>1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}
void modify(int u,int l,int r,int x,int c)		//将线段树x位置的数减c
{
	if(l==r) tr[u]-=c;
	else {
		int mid=l+r>>1;
		if(mid>=x) modify(u<<1,l,mid,x,c);
		else modify(u<<1|1,mid+1,r,x,c);
		tr[u]=max(tr[u<<1],tr[u<<1|1]);
	}
}
int query(int u,int l,int r,int x)				//查询线段树中大于等于x的最左边的值
{
	if(l==r) return l;
	int mid=l+r>>1;
	int ans=n;
	if(tr[u<<1]>=x) ans=query(u<<1,l,mid,x);	//如果左边存在大于等于x的值,则往左找
	else if(tr[u<<1|1]>=x) ans=query(u<<1|1,mid+1,r,x);
	return ans;
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		b.clear(); 
		scanf("%d%d",&n,&s);
		for(int i=1;i<=n;i++) scanf("%d",&w[i]);
        build(1,1,n);
		int k=0;					//存当前启用容器的数量
		for(int i=1;i<=n;i++)
		{
			int p=query(1,1,n,w[i]);//查询大于等于w[i]的最左边的位置
			if(p>k) k++;			//如果启用了新的容器则k++
			modify(1,1,n,p,w[i]);	//修改p位置的容量
		}

		b[s-w[1]]++;				//一开始只用了一个容器,并将w[1]放入
		for(int i=2;i<=n;i++)
		{
			map<int,int>::iterator p=b.lower_bound(w[i]);	//找到大于等于w[i]的最小值
			if(p==b.end()) b[s-w[i]]++;		//未找到则开新容器
			else {
                b[p->x]--;
                b[(p->x)-w[i]]++;
                if(b[p->x]==0) b.erase(p);
			}
		}
		int k2=0;
		for(PII it:b) k2+=it.y;
		printf("%d %d\\n",k,k2);
	}
	return 0;
}

以上是关于The 17th Zhejiang Provincial Contest B. Bin Packing Problem(线段树+map)的主要内容,如果未能解决你的问题,请参考以下文章

ZJCPC2020 第17届 浙江省赛The 17th Zhejiang Provincial Collegiate Programming Contest(ABCIK 5题)

The 18th Zhejiang University Programming Contest

The 15th Zhejiang Provincial Collegiate Programming Contest(部分题解)

The 18th Zhejiang Provincial Collegiate Programming Contest 补题记录(ACFGJLM)

The 19th Zhejiang Provincial Collegiate Programming Contest F - Easy Fix(主席树)

The 19th Zhejiang Provincial Collegiate Programming Contest F - Easy Fix(主席树)