P2101 命運石之門的選擇
題意
題目描述
在某一條不知名世界線的岡倫今天突然接到了一條\(dmail\),上面說世界線將會發生巨大變動,未來的他無論如何都無法扭轉這種變動回到原來的世界線。而世界線變動的原因是現在的他不久后錯過了與助手的約會。他約好要和助手去約會,但是在去約會之前,由于一直拖欠房租,房東大叔要求他幫忙完成一幅畫的上色,然而他沒有以最快的速度完成這個任務,導致他錯過了與助手的約會,從而導致世界線的劇變。現在到了拯救世界的時候,由于岡倫并不擅長畫畫,于是他找到了同樣不擅長畫畫的你來幫他解決這個問題(這是命運石之門的選擇)。不管怎樣現在拯救世界的重任交到了你的手上,而你雖然不擅長畫畫,但是你可以使用編程來幫助你解決這個問題。
這幅畫十分抽象:它由\(N\)個寬度為\(1\)高度為\(H_i\)的矩形組成,矩形并排排列,相鄰的矩形間沒有空隙,初始情況下每個矩形都是沒有顏色的。你有一個寬度為\(1\)的刷子,你可以豎直或水平的刷,每次使用刷子,你的刷子都必須保證一直全部處于矩形中,即不能刷到矩形以外的地方去,當然你每次刷的時候也不能拐彎。你每刷一次,要花費\(1\)的時間,這和刷的長度無關,比如你可以從最左邊刷到最右邊(當然是不經過矩形以外的部分),這也只花費\(1\)的時間。你的目的是將全部的矩形都涂滿顏色。請輸出這個最短的時間,以便岡倫決定是自己來完成這個任務還是讓你來做苦力。
輸入輸出格式
輸入格式:
第\(1\)行:一個正整數\(N\),表示矩形的個數。
接下來\(N\)個正整數\(H_i\),表示第\(i\)個矩形的高度。
輸出格式:
一個整數,表示最少花費的時間。
輸入輸出樣例
輸入樣例#1:
5
2 2 1 2 1
輸出樣例#1:
3
說明
【數據規模】
\(30\% N\leq 20, H_i\leq 100\)
\(60\% N\leq 100, H_i\leq 1000\)
\(100\% N\leq 5,000, H_i\leq 10^9\)
思路
這就是個簡單的分治或者\(dp\)啊。 --logeadd
完全不會分治的蒟蒻我只能做一些分治水題\(qwq\)。
我們對一個區間來搜索,再來一個一個區間地分下去。具體來說,我們用一個函數\(dfs(l,r)\)來查詢區間\([l,r]\)的答案,而這個答案又可以用多個子區間\([l,x_1],[x_1+1,x_2],[x_2+1,x_3]\cdots [x_y+1,r]\)得到,這樣我們一步步地分區間搜索,最后合并答案。
那么怎么合并答案呢?對于一個區間\([l,r]\)有一個顯然的結論:先把下面的方塊橫著涂滿,然后再豎著涂剩余的區間,所以我們可以統計出這個區間的最小高度,然后把它橫著涂滿,再把剩余的方塊\(dfs\)考慮就好了。
比方說我們有這樣的一個形狀:
■■ ■■ ■
■■ ■■ ■■
■■■■■■■■■■
■■■■■■■■■■
先把下面的方塊橫著涂:
■■ ■■ ■
■■ ■■ ■■
□□□□□□□□□□
□□□□□□□□□□
上方就多出來了三個小區間,再來分別\(dfs\)就好了。
AC代碼
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MAXN=5e3+5;
LL n,a[MAXN];
LL read()
{LL re=0;char ch=getchar();while(!isdigit(ch)) ch=getchar();while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();return re;
}
LL ask(LL l,LL r)
{if(l==r) return 1;LL re=INT_MAX;for(LL i=l;i<=r;i++) re=min(re,a[i]);for(LL i=l;i<=r;i++) a[i]-=re;for(LL i=l;i<=r;i++){if(!a[i]) continue;LL j=i;while(j<=r&&a[j+1]) j++;re+=ask(i,j),i=j;}return min(r-l+1,re);
}
int main()
{n=read();for(LL i=1;i<=n;i++) a[i]=read();printf("%lld",ask(1,n));return 0;
}