• [CodeForces-1225B] TV Subscriptions 【贪心】【尺取法】


    [CodeForces-1225B] TV Subscriptions 【贪心】【尺取法】

    标签: 题解 codeforces题解 尺取法


    题目描述

    Time limit
    2000 ms
    Memory limit
    262144 kB
    Source
    Technocup 2020 - Elimination Round 2
    Tags
    implementation  two pointers  *1300
    Site
    https://codeforces.com/problemset/problem/1225/B2

    题面

    CodeForces-1225B.png

    Example
    Input

    4
    5 2 2
    1 2 1 2 1
    9 3 3
    3 3 3 2 2 2 1 1 1
    4 10 4
    10 8 6 4
    16 9 8
    3 1 4 1 5 9 2 6 5 3 5 8 9 7 9 3

    Output

    2
    1
    4
    5

    题目大意

    给定(n, k, d)和序列(a[1 cdots n])(1 leq a[i] leq k)。问序列中连续(d)个数最少能有几个不同的数?

    例如,
    (n = 5, k = 2, d = 2, a[1 cdots 5] = [1, 2, 1, 2, 1])。连续的两个数只有([1, 2], [2, 1])两种情况都是包含2个不同的数。所以输出2;
    (n = 9, k = 3, d = 3, a[1 cdots 9] = [3, 3, 3, 2, 2, 2, 1, 1, 1])。当连续的三个数为([3, 3, 3])时,包含的不同数的个数最少,为1个。若为其他,如([2, 2, 1])([3, 2, 2]),则包含2个不同的数。所以输出1。


    解析

    这道题用到了尺取法,尺取法在Codeforces中的标签是two pointers(双指针法)

    但因为题目中规定了(d),即尺取的长度,两个指针只能一起移动,所以尺取的思想体现的不是很明显。

    既然尺取的左右两个指针(l, r)如何移动的问题已经解决,那么接下来要解决的就是怎么维护尺取区间的信息?这道题是询问区间所包含的不同数字数最小。我们通过观察发现,每次(l, r)向右移动一位其实区间中只有两个变化,一个是先前(l)所指的数字出队,另一个是当前(r)所指的数字入队。那么我们只需查看这两个数字的一出一入对队列中不同数字数产不产生影响就可以了。这样我们就通过数组(vis[1 cdots k])来记录尺取区间中每个数字出现的次数。

    • 如果((++ vis[a[r]]) = 1),那么不同数字数就加一。
    • 如果((-- vis[a[l - 1]]) = 0),那么不同数字数就减一。

    这样尺取区间从最左端滑到最右端,记录下不同数字数的最大值即是答案。该方法的时间复杂度是(O(n))

    因为有多组输入,所以我们每次都要给数组(vis)归零。注意,数组归零的时候,如果使用k,循环k次会TLE。


    通过代码

    /*
    Status
    	Accepted
    Time
    	31ms
    Memory
    	7836kB
    Length
    	937
    Lang
    	GNU G++11 5.1.0
    Submitted
    	2019-12-17 21:52:46
    RemoteRunId
    	67074221
    */
    
    
    #include <bits/stdc++.h>
    using namespace std;
    
    const int MAXN = 1e6 + 50;
    int vis[MAXN], a[MAXN], n, k, d;
    
    inline int read()       //快读,因为是2e5的输入量,所以加快读能明显提高程序速度.
    {
    	int res = 0, f = 1;
    	char ch;
    
    	ch = getchar();
    
    	while(!isdigit(ch)){
    		if(ch == '-')
    			f = -1;
    
    		ch = getchar();
    	}
    	while(isdigit(ch)){
    		res = (res << 3) + (res << 1) + ch - 48;
    		ch = getchar();
    	}
    
    	return f * res;
    }
    
    void mem0()            //vis数组归零函数.
    {
        for(int i = 1; i <= n; i ++)     //注意这里要循环n次而不是k次,否则会超时.
            vis[a[i]] = 0;
    
        return ;
    }
    
    int main()
    {
    	int times;
    	times = read();
    
    	while(times --){
    
    		int minn = 0, cnt = 0;
            mem0();
    
    		n = read(), k = read(), d = read();
    
    		for(int i = 1; i <= n; i ++){
    			a[i] = read();
    		}
    		for(int i = 1; i <= d; i ++){      //先将最左端的d个数加入区间,构造好尺取区间.
    			if(!vis[a[i]])	cnt ++;
    			vis[a[i]] ++;
    		}
    		minn = cnt;
    		for(int i = d + 1; i <= n; i ++){
    
    			if(!vis[a[i]])	cnt ++;         //注意这里并没有真的使用l,r指针(下标),因为l,r的移动是同时的,所以我们就用循环的i代替r,i-d代替l-1.
    			vis[a[i]] ++;
    
    			vis[a[i - d]] --;
    			if(!vis[a[i - d]])  cnt --;
    
    			minn = min(minn, cnt);      //每次移动一位之后,看是否可以更新最小值.
    
    		}
    
    		printf("%d
    ", minn);
    	}
    	return 0;
    }
    
    
    

  • 相关阅读:
    struts2 中的 result 返回类型是 json 的配置问题
    EL 表达式 取值将 < 转成 &1t 问题
    Redis 设计与实现读书笔记一 Redis List
    lhgdialog 与后台交互的对话框
    Redis 设计与实现读书笔记一 Redis字符串
    Struts2 OGNL使用详解(转)
    Struts2 中遇到的问题
    Struts2
    Linux服务器开发:工具
    fastadmin html数字验证
  • 原文地址:https://www.cnblogs.com/satchelpp/p/12068814.html
Copyright © 2020-2023  润新知