• 暑假集训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
    %d",sum,ans);
    	return 0;
    }
    
    
    如初见 与初见
  • 相关阅读:
    线程间协作的两种方式:wait、notify、notifyAll和Condition
    Lock
    线程池ExecutorService的使用
    使用volatile的条件
    解决共享资源竞争
    并发时捕获异常
    Executor执行器
    BufferedReader 和BufferedWriter
    彻底理解Java的Future模式
    贝叶斯网络简介--翻译版
  • 原文地址:https://www.cnblogs.com/HISKrrr/p/13227082.html
Copyright © 2020-2023  润新知