牆上海報

20505吳承樺

牆上海報

2022 1月 APCS

第四題

Zerojudge:

h084

題目敘述

有一個由 n 個木板所組成的柵欄,每個木板的高度為 h[1],h[2]...,h[n],

 

有 k 張海報要張貼在柵欄上,每張海報的寬度為 w[1],w[2],...w[n] 並且高度均為 1。

 

若要張貼海報在高度為 x 的高度,則第 i 張海報需要張貼在一個長度為 w[i] 的連續並且高度都不小於 x 的木板上,

 

且每張海報張貼的高度需要一致、按照順序並不能重疊 (可以相連)。詢問最高可以貼到多高的位置。

想法

二分搜

首先看到找最大最小值

我想到兩種

1.dp

2.二分搜

接著看一下題目的測資

n=2e5

h=1e9

可以推測-> 解答為 

O(n\ log\ n)

再來找單調性

二分搜

答案問高度

高度的單調性?

柵欄是從下往上長

所以假設高度i有柵欄 它下面也一定都有

一個高度若可以放完海報

它下面也一定可以放完

111110000

放海報

有幾個關鍵字要注意

1.每張海報張貼的高度需要一致

2.按照順序並不能重疊 (可以相連)

那這邊用的是貪心的想法

貪心

哪裡貪心?

按照順序放:不能挑想放的放

今天從左掃過來

所以如果遇到可以放的不放

那之後還得找地方放

有可能會擋到後面的放

結論:遇到長度符合就放

實作

#include<bits/stdc++.h>
#define maxn 200005
using namespace std;

int h[maxn],w[maxn],n,k;;
int l=1,r,mid;

bool check(int x){
    vector<int>v;
    int tmp=0;
    for(int i=1;i<=n;i++){
        if(h[i]>=x){
            tmp++;
        }
        else{
            v.push_back(tmp);
            tmp=0;
        }
    }
    if(tmp)v.push_back(tmp);
    int i=0,j=1;
    while(i<v.size() && j<=k){
        if(v[i]<w[j]){
            i++;
        }
        else if(v[i]>w[j]){
            v[i]-=w[j];
            j++;
        }else{
            i++;
            j++;
        }
    }
    if(j==k+1)return true;
    return false;
}
int main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>h[i];
        r=max(r,h[i]);
    }
    for(int i=1;i<=k;i++)cin>>w[i];
    while(l<r){
        mid=(l+r)/2;
        if(check(mid)){
            l=mid+1;
        }
        else{
            r=mid;
        }
    }
    cout<<r<<'\n';
}

第一份程式碼:10%

bool check(int x){
    vector<int>v;
    int tmp=0;
    for(int i=1;i<=n;i++){
        if(h[i]>=x){
            tmp++;
        }
        else{
            v.push_back(tmp);
            tmp=0;
        }
    }
    if(tmp)v.push_back(tmp);
    int i=0,j=1;
    while(i<v.size() && j<=k){
        if(v[i]<w[j]){
            i++;
        }
        else if(v[i]>w[j]){
            v[i]-=w[j];
            j++;
        }else{
            i++;
            j++;
        }
    }
    if(j==k+1)return true;
    return false;
}

第一份程式碼

checker()

來判斷這個高度可不可以放完

可以:true

else:false

 

程式邏輯沒有太大的錯誤

但作法過於冗長

繁複

int main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>h[i];
        r=max(r,h[i]);
    }
    for(int i=1;i<=k;i++)cin>>w[i];
    while(l<r){
        mid=(l+r)/2;
        if(check(mid)){
            l=mid+1;
        }
        else{
            r=mid;
        }
    }
    cout<<r<<'\n';
}

第一份程式碼

二分搜

也是造成錯誤的原因

這邊的r值是找最大高度 

左閉右開寫法

所以r值要++

找到的值是最小的0

題目是要最大的1

輸出r要-1

改完這些其實就可以通過了

但我後來發現有更簡潔的checker寫法

第二份-AC解

#include<bits/stdc++.h>
#define maxn 200005
using namespace std;

int h[maxn],w[maxn],n,k;;
int l=1,r,mid;

bool check(int x){
    int cnt=0,j=1;
    for(int i=1;i<=n;i++){
        if(h[i]>=x)cnt++;
        else cnt=0;
        if(cnt==w[j]){
            cnt=0;
            j++;
        }
        if(j==k+1)return true;
    }
    return false;
}
int main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>h[i];
        r=max(r,h[i]);
    }
    for(int i=1;i<=k;i++)cin>>w[i];
    r++;
    while(l<r){
        mid=(l+r)/2;
        if(check(mid)){
            l=mid+1;
        }
        else{
            r=mid;
        }
    }
    cout<<r-1<<'\n';
}

第二份-AC解

bool check(int x){
    int cnt=0,j=1;
    for(int i=1;i<=n;i++){
        if(h[i]>=x)cnt++;
        else cnt=0;
        if(cnt==w[j]){
            cnt=0;
            j++;
        }
        if(j==k+1)return true;
    }
    return false;
}

原本是把這層高度各個長度存在vector

再去找

但其實找到長度符合現在要放的海報長度

就可以直接放

感謝各位聆聽

題目分享

By wuchanghualeo

題目分享

  • 32