hdu2795 -- Billboard 线段树 详解

题目通道

hdu2795

题目大意

在一个广告牌中放通告,每个通告宽为wi,高为1,放通告时尽量往上放,再在此基础上尽可能靠左放。问每块通告放置的位置(无处可放则输出-1)。

题目思路

样例输入
3 5 5
2
4
3
3
3
放置位置如图:

到第5块的时候已经无处可放。

跟着模拟一遍放置,很容易想到基本的思路,暴力的解法是:
放置一块通告,从第1行到第h行枚举,如果当前行所剩余的空间能够放置该通告,则放置该通告并更新当前行的剩余空间。最坏的情况的时间复杂度是O(1+2+…+n)即O(n2),暴力枚举会TLE。

本题的核心是快速的找到最靠上的剩余空间能够放置当前通告的那一行。二分是优化暴力枚举的利器。

如果左区间内存在能够放置当前通告的行,则递归左区间寻找,否则,递归右区间寻找。判断一个区间是否存在某一行能够放置当前通告的依据是:该区间的剩余容量最大的那一行能否容纳该通告。则问题变成了查询区间的最大值,判断其是否大于等于当前通告的宽度。查询、维护区间最大值,是非常典型的线段树的应用,采用线段树,整体时间复杂度为O(nlog2n)。

AC代码

#include <iostream>
#include <cstdio>
using namespace std;
#define maxn 200000 + 10

struct node {
	int l, r;
	int maxval;
}tree[4 * maxn];

int h, w, n;

int get_max(int a, int b) {
	if (a > b) {
		return a;
	}
	return b;
}

void build_tree(int o, int l, int r) {
	tree[o].l = l;
	tree[o].r = r;
	if (l == r) {
		tree[o].maxval = w;
		return;
	}
	int mid = (l + r) >> 1;
	build_tree(o << 1, l, mid);
	build_tree(o << 1 | 1, mid + 1, r);
	tree[o].maxval = get_max(tree[o << 1].maxval, tree[o << 1 | 1].maxval);
}

int put_billboard(int o, int wi) {
	if (tree[o].l == tree[o].r) {
		tree[o].maxval -= wi;
		return tree[o].l;
	}
	int put_pos;
	//如果左区间存在剩余空间大于等于wi的行,优先去左区间寻找
	if (tree[o << 1].maxval >= wi) {
		put_pos = put_billboard(o << 1, wi);
	}
	else {
		put_pos = put_billboard(o << 1 | 1, wi);
	}
	tree[o].maxval = get_max(tree[o << 1].maxval, tree[o << 1 | 1].maxval);
	return put_pos;
}
int main()
{
	while (~scanf("%d%d%d", &h, &w, &n)) {
		int wi;
		//空间优化,最多放置n行
		h = h > n ? n : h;
		build_tree(1, 1, h);
		while (n--) {
			scanf("%d", &wi);
			int ans = -1;
			//如果存在能够放置当前通告的行,才去寻找
			if (wi <= tree[1].maxval) {
				ans = put_billboard(1, wi);
			}
			printf("%d\n", ans);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值