一、地址
二、题意
- 有一块高为h,宽为w的面板,要向该面板放n个公告。
- 每个公告高为1,长度为wi,优先放置最上面的最左边。
- 求每块公告放置在面板上的行号,如放不下输出-1。
三、解题思路:
- 虽然h的范围比较大,但是只有n个数据,所以可以剪枝,如果h大于n,h可以等于n。
- 正常想法,从上往下遍历,每次比较是否有空余的位置,双重循环会超时。
- 从上到下依次放公告,可以说明是连续的,所以可以考虑线段树,对面板高度进行线段长度维护。
- 将宽度值存起来,每次从左子树遍历玩下走,直到符合要求可以返回。
- 这里必须注意一个剪枝:
- 找到了符合要求的点之后记得向上维护子节点的最大长度。
- 下次遍历时如果父节点都小于要查询的长度,直接返回。
四、AC代码
#include <iostream>
#include <algorithm>
#include <cmath>
#include <string>
#include <cstring>
using namespace std;
int h, w, n, p, answer;
const int NUM = 200000;
struct SegementTree
{
int l, r;
int count;
}Tree[NUM * 4];
void CreateSegmentTree(int l, int r, int x)
{
Tree[x].l = l;
Tree[x].r = r;
Tree[x].count = w;
if(l >= r)
{
return;
}
int mid = (l + r) / 2;
CreateSegmentTree(l, mid, x << 1);
CreateSegmentTree(mid + 1, r, x << 1 | 1);
}
void MaintainSegmentTree(int x, int value)
{
if(Tree[x].count < value)
return;
if(answer != -1)
return;
if(Tree[x].l == Tree[x].r)
{
if(Tree[x].count >= value)
{
Tree[x].count -= value;
answer = Tree[x].l;
}
return;
}
MaintainSegmentTree(x << 1, value);
MaintainSegmentTree(x << 1 | 1, value);
if(answer != -1)
{
Tree[x].count = max(Tree[x << 1].count, Tree[x << 1 | 1].count);
}
}
int main()
{
while(scanf("%d %d %d", &h, &w, &n) != EOF)
{
if(h > n)h = n;
CreateSegmentTree(1, h, 1);
for(int i = 0; i < n; i++)
{
scanf("%d", &p);
answer = -1;
MaintainSegmentTree(1, p);
printf("%d\n", answer);
}
}
return 0;
}