题目通道
题目大意
在一个广告牌中放通告,每个通告宽为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;
}