zoukankan      html  css  js  c++  java
  • 单调队列入门之烽火传递

    有N个数(N<=100000) ,在连续M(M<=N)个数里至少要有一个数被选择. 求选出来数的最小总和。

    输入
    第一行两个整数 N,M 接下来N行 Wi(Wi<=100) 表示第i个数

    输出
    一个整数,最小总和

    样例
    输入
    5 3
    1
    2
    5
    6
    2
    输出
    4
    提示
    2+2=4

    Sol:
    定义f[i]为一定选择第i个数字,且保证前i个数字都满足题意的最小代价
    则可以写出这样的方程f[i]:=min{f[j]}+a[i](i-m<=j<=i-1)
    (因为要保证从i向左数的m个数字均要被覆盖)
    于是对于样例
    输入数字 1 2 5 6 3 4 1
    f数组 1 2 5 7 5 9 6
    则对于6来说, f[4]=min(1,2,5)+6=7
    则对于3来说, f[5]=min(2,5,7)+3=5
    则对于4来说, f[6]=min(5,7,5)+4=9
    则对于1来说, f[4]=min(7,5,9)+1=6
    由于覆盖第n个数字,我们可以选择f[n-m+1]...f[n]中的最小值
    于是扫一遍就好了。
    暴力程序如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 100010;
    int n, m, q[N], h = 1, t, num[N], a[N << 1], ans, f[N << 1];
    int main() 
    {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; ++i) 
    	     scanf("%d", &a[i]);
        memset(f, 0x3f, sizeof(f));
        for (int i = 1; i <= m; ++i) 
    	     f[i] = a[i];
        for (int i = m + 1; i <= n; i++)
        {
        
            for (int j = i - m; j < i; j++) 
    		//要从[i-m,i-1]这段中选个最小的f[j]+a[i]出来
    		    f[i] = min(a[i] + f[j], f[i]);
    	}
    	ans = 0x3f3f3f3f;
        for (int i = n - m + 1; i <= n; i++) 
        //如果要覆盖第n个city,则从[n-m+1,n]中选择 
    	    ans = min(ans, f[i]);
        printf("%d", ans);
        return 0;
    }
    

      


    发现此题关键是在某个大小固定的区间求一个极小值,于是可以单调队列之

    #include <cstdio>
    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    inline int read();
    const int N = 2e5 + 1;
    int n, m, h, t;
    int a[N], q[N];
    int f[N];
    void work() 
    {
        h = 0; 
    	t=0;//注意h的初值为0 
    	f[0]=0;
        for (int i = 1; i <= n; i++) 
        {
            while (q[h] < i - m && h <= t) 
    		       h++;
            f[i] = f[q[h]] + a[i];  
           //单调上升队列,取[i-m,i-1]这个区间中值最小的f值
            while (f[i] <= f[q[t]] && h <= t) t--;
            q[++t] = i; //维护一个单调上升的f值组成的数列,注意不是a值
        }
    }
    int main() 
    {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) 	
    	    scanf("%d", &a[i]);
        work();    
    	int ans = 1e9;
        for (int i = n - m + 1; i <= n; i++) 
    	    ans = min(ans, f[i]);
        printf("%d", ans);
        return 0;
    }
    

      

    绿色通道
    高二数学《绿色通道》总共有N道题目要抄,编号1..N,抄第i 题要花ai分钟。小 Y 决定只用不超过t 分钟抄这个,因此必然有空着的题。每道题要么不写,要么抄完,不能写一半。下标连续的一些空题称为一个空题段,它的长度就是所包含的题目数。这样应付自然会引起马老师的愤怒,最长的空题段越长,马老师越生气。现在,小 Y想知道他在这t分钟内写哪些题,才能够尽量减轻马老师的怒火。由于小 Y 很聪明,你只要告诉他最长的空题段至少有多长就可以了,不需输出方案。

    输入
    第一行为两个整数n,t 。 第二行为n个整数,依次为a1....an 。

    0<N<=50000 0<ai<=3000 0<t<10^8

    输出
    输出一个整数,表示最长的空题段至少有多长。

    样例
    输入
    17 11
    6 4 5 2 5 3 4 5 2 3 4 5 2 3 6 3 5
    输出
    3

    Sol:同上一题,二分枚举下答案即可

    /*
    设k为最长的空题数
    问题转换为每k+1个数字至少要取一个数,
    取出的数字之和小于等于t
    二分枚举k即可
    */
    
    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    const int N = 5e4 + 50;
    const int inf = 0x3f3f3f3f;
    
    int n, t, l, r, mid, ans;
    int a[N], q[N], f[N];
    int head, tail;
    
    bool check(int mid) 
    {
        memset(f, 0, sizeof(f));
        memset(q, 0, sizeof(q));
        head = tail = 1;
        for (int i = 1; i <= n; i++) 
    	{
            while (head <= tail && q[head] < i - mid - 1) 
    		     head++;
            f[i] = f[q[head]] + a[i];
            while (head <= tail && f[i] <= f[q[tail]]) 
    		     tail--;
            q[++tail] = i;
        }
        int ans = inf;
        for (int i = n; i >= n - mid; i--) 
    	{
            if (f[i] <= t)
                return 1;
        }
        return 0;
    }
    
    int main() {
        scanf("%d%d", &n, &t);
        for (int i = 1; i <= n; i++) 
    	scanf("%d", &a[i]);
    
        int l = 1, r = n;
        while (l <= r) 
    	{
            int mid = (l + r) >> 1;
            if (check(mid))
                r = mid - 1;
            else
                l = mid + 1;
        }
        printf("%d
    ", r + 1);
    
        return 0;
    }
    

      

  • 相关阅读:
    微信小程序 获取手机号 获取基础信息 自定义分享 获取dom节点
    微信公众号 生成带参数进入公众号关注页的二维码 监听用户关注公众号事件 自定义菜单栏 (服务端)
    js 防抖和节流 (学习笔记)
    vue 封装axios api模块化 并使用方式 MD5 数据加密
    1.1 Logistics Regression模型
    音视频入门-19-使用giflib处理GIF图片
    思维
    面试题库
    前端代码规范
    D3JS笔记
  • 原文地址:https://www.cnblogs.com/cutemush/p/13627741.html
Copyright © 2011-2022 走看看