2020浙江省赛-Bin Packing Problem-(线段树)

B

题意:
就是有n个物品,然后有体积为m的盒子,然后有两种算法。第一种,顺序遍历每个物品,找到第一个能装下它的盒子,如果没有,那么就新添一个盒子。第二种,顺序遍历每个物品,找到能装下它的提及剩余最小的盒子,如果没有,那么就新添一个盒子。问你两种算法分别要多少种盒子。

思考:
对于第二种很简单,就是multiset跑一遍就行了。对于第一种,刚开始我还想vector,但是vector要满足是升序的呀。所以就想到了线段树,如果最大值都装不下,那么新开一个盒子。如果能装下,那么就去装,对于怎么找到第一个大于它的点,刚开始我想了个二分找最近点,但是nloglog应该是超时的。一想既然就是单点修改,直接写个change函数,尽量往左走,最后修改就行了呀。还有一点就是,对于change和update函数,之类的,后面都要加上pushup,如果有pushdown,那么每个函数都要有pushup。

代码:

#include<bits/stdc++.h>
#define int long long
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define node_l node<<1
#define node_r node<<1|1

using namespace std;

const int inf = 1e18;
const int N = 1e6+10,M = 2000;

struct Node{
	int L,R;
	int sum;
	int maxn;	
}t[4*N];

int T,n,m;
int va[N];

void build(int node,int l,int r)
{
	t[node].sum = t[node].maxn = 0;
	t[node].L = l,t[node].R = r;
	if(l==r)
	{
		return ;
	}
	int mid = (l+r)>>1;
	build(node_l,l,mid);build(node_r,mid+1,r);
}

void update(int node,int x,int value)
{
	if(t[node].L==x&&t[node].R==x)
	{
		t[node].sum = value;
		t[node].maxn = value;
		return ;
	}
	int mid = (t[node].L+t[node].R)>>1;
	if(x<=mid) update(node_l,x,value);
	else update(node_r,x,value);
	t[node].maxn = max(t[node_l].maxn,t[node_r].maxn);
}

int query(int node,int l,int r)
{
	if(t[node].L>=l&&t[node].R<=r) return t[node].maxn;
	int mid = (t[node].L+t[node].R)>>1;
	if(r<=mid) return query(node_l,l,r);
	else if(l>mid) return query(node_r,l,r);
	else return max(query(node_l,l,mid),query(node_r,mid+1,r));
}

void change(int node,int ned)
{
	if(t[node].L==t[node].R)
	{
		t[node].sum -= ned;
		t[node].maxn -= ned;
		return ;
	}
	if(t[node_l].maxn>=ned) change(node_l,ned);
	else change(node_r,ned);
	t[node].maxn = max(t[node_l].maxn,t[node_r].maxn);
}

int solve1()
{
	int idx = 0;
	build(1,1,n);
	for(int i=1;i<=n;i++)
	{
		if(!idx||query(1,1,idx)<va[i])
		{
			update(1,++idx,m-va[i]);
			continue;
		}
		change(1,va[i]);
	}
	return idx;
}

int solve2()
{
	int ans = 0;
	multiset<int > s;
	for(int i=1;i<=n;i++)
	{
		auto idx = s.lower_bound(va[i]);
		if(idx==s.end())
		{
			ans++;
			s.insert(m-va[i]);
		}
		else
		{
			int now = *idx;
			s.erase(s.find(now));
			s.insert(now-va[i]);
		}
	}
	return ans;
}	

signed main()
{
	IOS;
	cin>>T;
	while(T--)
	{
		cin>>n>>m;
		for(int i=1;i<=n;i++) cin>>va[i];
		cout<<solve1()<<" "<<solve2()<<"\n";
	}
	return 0;
}

总结:
多多思考呀。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值