HDU 2795 Billboard

一、地址

  • 题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=2795
  • GitHub:https://github.com/lamborghini1993/ACMTopic

二、题意

  • 有一块高为h,宽为w的面板,要向该面板放n个公告。
  • 每个公告高为1,长度为wi,优先放置最上面的最左边。
  • 求每块公告放置在面板上的行号,如放不下输出-1。

三、解题思路:

  • 虽然h的范围比较大,但是只有n个数据,所以可以剪枝,如果h大于n,h可以等于n。
  • 正常想法,从上往下遍历,每次比较是否有空余的位置,双重循环会超时。
  • 从上到下依次放公告,可以说明是连续的,所以可以考虑线段树,对面板高度进行线段长度维护。
  • 将宽度值存起来,每次从左子树遍历玩下走,直到符合要求可以返回。
  • 这里必须注意一个剪枝:
    • 找到了符合要求的点之后记得向上维护子节点的最大长度。
    • 下次遍历时如果父节点都小于要查询的长度,直接返回。

四、AC代码

#include 
#include 
#include 
#include 
#include 
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;//剪枝h
        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;
}

你可能感兴趣的:(算法)