1. 程式人生 > 實用技巧 >暑假集訓Day 9 WYT的刷子

暑假集訓Day 9 WYT的刷子

題目大意

WYT有一把巨大的刷子,刷子的寬度為M米,現在WYT要使用這把大刷子去粉刷有N列的柵欄(每列寬度都為1米;每列的高度單位也為米,由輸入資料給出)。

使用刷子的規則是:

1.與地面垂直,從柵欄的底部向上刷
2.每次刷的寬度為M米(當剩餘柵欄寬度不夠M米的話,刷子也可以使用,具體看樣例2)
3.對於連續的M列柵欄,刷子從底向上,刷到的高度只能到這M列柵欄的最低高度。

WYT請你回答兩個問題:

1.最少有多少個單位面積不能刷到(單位面積為1平米)
2.在滿足第一問的條件下,最少刷幾次?

輸入格式

共兩行:

第一行兩個整數N和M。

第二行共N個整數,表示N列柵欄的高度

輸出格式

一行,兩個整數,分別為最少剩餘的單位面積數量和最少刷的次數。

樣例

樣例輸入

5 3
5 3 4 4 5

樣例輸出

3
2

演算法分析

  • 先來求解第一問: 如何判斷當前位置的柵欄是否可以刷完呢? 根據題意我們可以知道在一個刷子的範圍內 是由最矮的柵欄控制高度的 所以如果當前位置的柵欄向左向右的柵欄不低於它的個數大於等於M,則當前位置的柵欄就可以刷完
  • 所以用兩個單調的佇列來維護一下當前位置可以向右延伸的距離與可以向左延伸的距離
int tail = -1;//求向右延伸的寬度
for(int i = 1;i <= n+1;++i){//列舉到n+1可以使前n個都出列
	while(tail >= 0 && a[i] < a[que[tail]]){//維護一個單調不上升的佇列 如果第i個的高度比前一個矮了就出隊
		r[que[tail]] = i - que[tail];//r是當前節點所能向右延伸的距離
		--tail;
	}
	que[++tail] = i;//把當前位置入隊
}
tail = -1;//求向左延伸的寬度
for(int i = n;i >= 0;--i){
	while(tail >= 0 && a[i] < a[que[tail]]){
		l[que[tail]] = que[tail] - i;
		--tail;
	}
	que[++tail] = i;
}
for(int i = 1;i <= n;++i){
	if(l[i] + r[i] - 1 >= m)flag[i] = true;//flag標記能刷完
}

  • 然後就是求不能刷到的方塊個數了 我們可以用一個數組mh表示當前方格能刷到的最高位置 遞推處理一下:mh[i] = mdi-1(很顯然的關係式 要麼能刷完,如果刷不完的話肯定就等於前面的矩形能刷的高度 否則的話如果大於那會出現刷子卡在前面那個過不來 如果小於那麼前面那個就會被它卡住~~語言表達有點困難 請盡力理解一下 ~~)然後再從右向左處理一遍(因為有可能可以將當前位置安在右邊一起刷完可能會更優)
  • 已知mh那顯然不能刷的方塊個數也就出來了 見程式碼
for(int i = 1;i <= n;++i){
	if(flag[i])mh[i] = a[i];
	else mh[i] = mh[i-1];
}
for(int i = n;i > 0;--i){
	if(!flag[i])mh[i] = max(mh[i],mh[i+1]);
	sum -= mh[i];
}
  • 要求刷的次數 直接貪心求解就好了(因為如果能刷高一定會往高裡刷 如果幾個一樣高度的能一起刷 那就肯定會一起刷掉)
int k = 2,ans = 0;//k=2是因為第一個方塊無論如何都要花費一次去刷
while(k <= n){//當前刷到第k個方塊了
	int cnt = 1;//第一個方塊已經花費一次去刷了
	while(k < n && mh[k] == mh[k+1]){//如果當前的能刷的最高高度和下一個能刷的最高高度一樣 就一起刷掉
		cnt++;k++;
	}
	ans += cnt/m;
	if(cnt % m)++ans;//保證向上取整
	++k;//進入下個矩形
}

程式碼展示

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e7+10;
typedef long long ll;
ll a[maxn],sum,r[maxn],l[maxn],mh[maxn],que[maxn],flag[maxn];

int main(){
	int n,m;scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;++i){scanf("%lld",&a[i]);sum += a[i];}
	int tail = -1;
	for(int i = 1;i <= n+1;++i){
		while(tail >= 0 && a[i] < a[que[tail]]){
			r[que[tail]] = i - que[tail];
			--tail;
		}
		que[++tail] = i;
	}
	tail = -1;
	for(int i = n;i >= 0;--i){
		while(tail >= 0 && a[i] < a[que[tail]]){
			l[que[tail]] = que[tail] - i;
			--tail;
		}
		que[++tail] = i;
	}
	for(int i = 1;i <= n;++i){
		if(l[i] + r[i] - 1 >= m)flag[i] = true;
	}
	for(int i = 1;i <= n;++i){
		if(flag[i])mh[i] = a[i];
		else mh[i] = mh[i-1];
	}
	for(int i = n;i > 0;--i){
		if(!flag[i])mh[i] = max(mh[i],mh[i+1]);
		sum -= mh[i];
	}
	int k = 2,ans = 0;
	while(k <= n){
		int cnt = 1;
		while(k <= n+1 && mh[k] == mh[k-1]){
			cnt++;k++;
		}
		ans += cnt/m;
		if(cnt % m)++ans;
		++k;
	}
	printf("%lld\n%d",sum,ans);
	return 0;
}