群里一个算法题

  题目:

  某同学拿到一组数列 $ a_1, a_2, \cdots , a_n $,他想知道存在多少个区间 $ [l, r] $ 同时满足下列两个条件:

    1. $ r - l + 1 = k $;

    2. 在 $ a_l, a_{l + 1}, \cdots , a_r $ 中,存在一个数至少出现 t 次。

  要求要做的就是输出满足条件的区间个数

  input

    第一行输入三个整数 $ n, k, t (1 \leqslant n, k, t \leqslant 10^5) $

    第二行输入 n 个整数, $ a_1, a_2, \cdots, a_n(1 \leqslant a_i \leqslant 10^5) $

  output

    输出区间个数。

  Example

  input

5 3 2
3 1 1 1 2

  output

3

  hint

  样例中,因为区间 $ [1, 3] $ 中 1 出现了 2 次,区间 $ [ 2, 4] $ 中 1 出现了 3 次, 区间 $ [3, 5] $ 中 1 出现了 2 次,所以一共有 3 个区间满足条件。

 

  这是在群里看到的一个题,感觉应该是一个dp题,但由于我太渣,推半天转移方程推不出来。。。决定先写一个暴力的试试,或者感觉可以维护一个区间树、树状树或者线段树。。。

  方法一:暴力算法太暴力,摒弃了。。。

  方法二:下面是一个简单的模拟算法。。

#include <iostream>
#include <minmax.h>
const int N = 100001;
using namespace std;
int main()
{
	int n, k, t, dp;
	int a[N], b[N];
	while (~scanf_s("%d %d %d", &n, &k, &t))
	{
		dp = 0;
		memset(b, 0, sizeof(b));
		for (int i = 0; i < n; i++)
		{
			cin >> a[i];
			b[a[i]]++;
			dp = max(dp, b[a[i]]);
		}
		if (k == 1) cout << n << endl;
		else if (t == 1) cout << n - k + 1 << endl;
		else if (k == n) if (dp == t) cout << "1" << endl;
			else cout << "0" << endl;
		else
		{
			dp = 0;
			for (int j = 0; j < n - k + 1; j++)
			{
				memset(b, 0, sizeof(b));
				for (int i = j; i < k + j; i++)
				{
					int c = ++b[a[i]];
					b[0] = max(b[0], c);
				}
				if (b[0] >= t) dp++;
			}
			cout << dp << endl;
		}
	}
	return 0;
}

  测试效果:

5 4 4
3 1 1 1 2
0
5 4 4
1 1 1 1 1
2
5 4 4
2 1 1 1 1
1
5 4 3
3 1 1 1 2
2
5 5 4
3 1 1 1 1
1
5 5 4
3 1 1 1 2
0
6 3 2
2 1 1 2 1 2
4
6 3 3
2 1 1 2 1 2
0
6 4 2
2 1 1 2 1 2
3
6 4 3
2 1 1 2 1 2
1

  算法分析:

  算法处理了几种情况的边界问题,以加快对边界情况的效率。(下面算法不考虑 memset() 的时间费用)

  case 1.显然当区间 $ k = 1 $ 时,$ t = 1 $ 才符合输入条件,如果 $ t \neq 1 $ , 则不存在这样的区间满足条件。时间复杂度 $ O(1) $;

  case 2.当 $ t = 1 $ 时,我的答案是输出按 $ k $ 划分的区间个数即可,这是显然的。所以答案是 $ n - k + 1 $ 。时间复杂度 $ O(1) $;

  case 3.当 $ k = n $ 时,我们只需要知道给出的 $ n $ 个数中是否存在一个出现次数等于 t 的数,如果存在则输出 1,不存在输出 0 。时间复杂度 $ O(1) $;

  case 4.当 $ k = t $ 时,我们就只需要判断每个 $ k $ 区间中,是否存在一个数的出现次数等于 t,如果存在则 +1 。 时间复杂度 $ O((n - k) \cdot k) = O(n \cdot k) $

  case 5.其他情况,即 $ n \neq k \neq t \neq 1 $ 时,我用了一个 $ O((n - k) \cdot k^2) $ 的算法来移动区间并计算 dp。

  case 4.其他情况,一个时间复杂度为 $ O((n - k)\cdot k) = O(n \cdot k - k^2) = O(n \cdot k) $ 的算法,空间复杂度 $ O(n) $。

  有趣的是,题目要求输入的 $ a_i \geq 1 $ 所以,我可以放心的使用 $ b[0] $ 来记录每个区间出现最多的数的次数。

  方法三:树状数组。

  思路:将重复出现的数用 1 表示在树状数组中,只出现过一次的就置为 0,然后区间查询即可。

  算法复杂度

    空间复杂度:$ O(n) $。常数比前一个算法大一点儿。

    时间复杂度:预处理部分花费 $ O(n) $ 开销,每次需要检查所有区间,最外层循环 $ n - k $,然后,每个区间查询利用树状数组的 `query()` 开销 $ O(lgk) $,故而整体的开销为 $ O(n \cdot lgk) $。

  代码:

#include <bits/stdc++.h>
using namespace std;
#define lowbits(x) (x&(-x))
const int N = 1000010;
int id[N], has[N], fenwick[N];
void upd(int n, int k, int x){while (k <= n) fenwick[k] += x, k += lowbits(k);}
int sum(int k)
{
    int ans = 0;
    while (k > 0) ans += fenwick[k], k -= lowbits(k);
    return ans;
}
int ask(int l, int r){return sum(r) - sum(l - 1);}
int main()
{
    int n, k, t, dots;
    memset(fenwick, 0, sizeof(fenwick));
    memset(has, 0, sizeof(has));
    cin >> n >> k >> t;
    for (int i = 1; i <= n; i++)
    {
        cin >> id[i];
        if (!has[id[i]])
            has[id[i]] = i;
        else {
            if (!fenwick[has[id[i]]])
                upd(n, has[id[i]], 1);
            upd(n, i, 1);
        }

    }
    dots = 0;
    for (int i = 1; i <= n - k + 1; i++)
        if (ask(i, k + i - 1) >= t) dots++;
    cout << dots << endl;
    return 0;
}

  

转载于:https://www.cnblogs.com/darkchii/p/9602184.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
4S店客户管理小程序-毕业设计,基于微信小程序+SSM+MySql开发,源码+数据库+论文答辩+毕业论文+视频演示 社会的发展和科学技术的进步,互联网技术越来越受欢迎。手机也逐渐受到广大人民群众的喜爱,也逐渐进入了每个用户的使用。手机具有便利性,速度快,效率高,成本低等优点。 因此,构建符合自己要求的操作系统是非常有意义的。 本文从管理员、用户的功能要求出发,4S店客户管理系统中的功能模块主要是实现管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理,用户客户端:首页、车展、新闻头条、我的。门店客户端:首页、车展、新闻头条、我的经过认真细致的研究,精心准备和规划,最后测试成功,系统可以正常使用。分析功能调整与4S店客户管理系统实现的实际需求相结合,讨论了微信开发者技术与后台结合java语言和MySQL数据库开发4S店客户管理系统的使用。 关键字:4S店客户管理系统小程序 微信开发者 Java技术 MySQL数据库 软件的功能: 1、开发实现4S店客户管理系统的整个系统程序; 2、管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理等。 3、用户客户端:首页、车展、新闻头条、我的 4、门店客户端:首页、车展、新闻头条、我的等相应操作; 5、基础数据管理:实现系统基本信息的添加、修改及删除等操作,并且根据需求进行交流信息的查看及回复相应操作。
现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本微信小程序医院挂号预约系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此微信小程序医院挂号预约系统利用当下成熟完善的SSM框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的MySQL数据库进行程序开发。微信小程序医院挂号预约系统有管理员,用户两个角色。管理员功能有个人中心,用户管理,医生信息管理,医院信息管理,科室信息管理,预约信息管理,预约取消管理,留言板,系统管理。微信小程序用户可以注册登录,查看医院信息,查看医生信息,查看公告资讯,在科室信息里面进行预约,也可以取消预约。微信小程序医院挂号预约系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值