<题目链接>
题目大意:
有一块h*w的矩形广告板,要往上面贴广告,然后给n个1*wi的广告,要求把广告贴上去,而且要求广告要尽量往上贴并且尽量靠左, 求第n个广告的所在的位置,不能贴则为-1。
解题分析:
先抛开本题的数据范围不谈,本题的思路就变的十分简单,无非就是每输入一个纸条的长度,判断一下该广告牌上剩余长度最大的层的剩余长度是否大于该纸条长度,若大于,则该层剩余长度要减去这个纸条的长度,若小于,则输出-1。
但很不幸,本题的数据范围十分之大,如果直接暴力查找剩余长度最大的层,毫无疑问会超时,于是这里要用到线段树。线段树作用也是快速查找剩余长度最大的层,明白了这一点,本题就可以做了。具体实现见代码(附有详细解析)
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; #define L l,m,rt<<1 #define R m+1,r,rt<<1|1 //u*2+1 const int N = 200000 + 10; int h, w, n; int a[N << 2]; void build(int l, int r, int rt) //建立线段树的规则为,最后叶子的序号与层数的序号相对应,比如第一片叶子表示第一层…… 每一个节点的区间范围表示对应的层数范围 { //若a[rt]表示的是叶子,则a[rt]的值为对应层剩余的长度 a[rt] = w; //因为开始每一层都是剩余w的长度,所以每一个节点都初始化为w //若a[rt]表示的是节点,则a[rt]的值表示该节点对应区间(层数)的剩余长度的最大值(在下面query()函数的过程中有体现) if (l == r) return; int m = (l + r) >> 1; build(L); build(R); } int query(int x, int l, int r, int rt) { if (l == r) { a[rt] -= x; //由于题目说了在每一层,纸条是优先放在最左边(优先放最右边也一样),所以,对于这种连续的放置,就可以直接转化为数值的加减就可以,如果题目规定了纸条放置的不同规则,那么纸条的放置则可能需要模拟了 return l; } int m = (l + r) >> 1; int res = (a[rt << 1] >= x) ? query(x, L) : query(x, R); //优先放在上层 a[rt] = max(a[rt << 1], a[rt << 1 | 1]); //递归到叶子后,由于该叶子对应层的剩余长度已经改变,所以线段树对应路径上的节点的值也应该随之改变,同时可以根据这里可以看出,该线段树每个节点存储的值为该节点对应区间(对应层数)中剩余长度的最大值 return res; } int main() { while (~scanf("%d%d%d", &h, &w, &n)) { if (h>n) h = n; build(1, h, 1); while (n--) { int x; scanf("%d", &x); if (a[1]<x) //a[i]存放的是第i层所能存放的最长长度,因此a[1]表示所有层中剩余长度最长的长度,因为此线段树的节点与叶子的关系为,a[rt] = max(a[rt << 1], a[rt << 1 | 1]); printf("-1 "); else printf("%d ", query(x, 1, h, 1)); } } return 0; }
代码二:
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; const int maxn=200000+1000; #define ll long long ll h,w,n; ll tr[maxn*4]; void Pushup(int rt){tr[rt]=max(tr[rt<<1],tr[rt<<1|1]);} void buildtree(int rt,int l,int r){ if(l==r){ tr[rt]=w; return; } int mid=(l+r)>>1; buildtree(rt<<1,l,mid); buildtree(rt<<1|1,mid+1,r); Pushup(rt); } int Insert(int rt,ll w,int l,int r){ //将广告牌插入,顺便更新线段树 if(l==r){ tr[rt]-=w; return l; } int mid=(l+r)>>1; int ans=(w<=tr[rt<<1])?Insert(rt<<1,w,l,mid):Insert(rt<<1|1,w,mid+1,r); Pushup(rt); return ans; } int main(){ while(scanf("%lld %lld %lld",&h,&w,&n)!=EOF){ if(h>n){ h=n;} int height=h; buildtree(1,1,height); for(int i=1;i<=n;i++){ ll a; scanf("%lld",&a); if(a>tr[1]){ printf("-1 "); continue; } printf("%d ",Insert(1,a,1,height)); } } return 0; }