第八届蓝桥杯个人赛省赛(软件类)C++B组试题第十题

一【题目描述】


标题: k倍区间

给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。  

你能求出数列中总共有多少个K倍区间吗?  

输入
-----
第一行包含两个整数N和K。(1 <= N, K <= 100000)  
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)  

输出
-----
输出一个整数,代表K倍区间的数目。  


例如,
输入:
5 2
1  
2  
3  
4  
5  

程序应该输出:
6

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 2000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include <xxx>
不能通过工程设置而省略常用头文件。

提交程序时,注意选择所期望的语言类型和编译器类型。

二【解题思路】

      看到k倍区间的标题,就想到了最大子序列和最大子串,可能会有关联,比较也都可以用循环判断。所以读完题目之后想到可以用枚举,for循环从第一个数字一直到最后一个数字,内层的for循环则进行累加判断,满足条件加一,判断下一个,不满足则退出,继续从第二个数字开始循环判断。看了一下数据规模,这样做的话应该要考虑优化才可以通过。但是对于查找优化(二分这些都不清楚)。

#include<iostream>
using namespace std;
int main()
{
	int n,k;
	int a[100000];
	cin>>n>>k;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
	}
	int sum=0;
	int ans = 0;
	for(int i=0;i<n;i++)
	{
		if(a[i]%k==0) ans++;//首先判断a[i]是不是,如果是,那么加一;
		sum = a[i];
		for(int j=i+1;j<n;j++)
		{	
			sum +=a[j]; 	
			if(sum%k==0) 
			{
				ans++;
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}

     但是上面的代码只可以解决一部分的数据规模的问题,所以我们考虑优化,我们可以考虑前缀和,在赋值的时候就可以添加前缀和,这样在for循环里面就不用重复的去计算求和的这一部分。可是这样的优化还是不够的,所以我们考虑对前缀和进行k取模操作,然后可以统计相同的余数的个数有多少,然后这样余数相同的和两两相减,肯定是k的倍数。比如5%2=1,7%2=1,那么(7-2)%2==0.所以我们开始码代码吧。

三【解题步骤】

#include<iostream>
#include<map>//这里用map记录余数相同的个数
using namespace std;
int a[100000];//录入的数字
int s[100000];//保存前缀和
map<int,int>cnt;
int main()
{
	int n,k;
	cin>>n>>k;//处理输入
	s[0]=0;
	cnt[0]=1;//这里s[0]等于0,那么肯定存在一个数余数是0,所以这里初始化为1
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		s[i]=(s[i-1]+a[i])%k;//处理前缀和
		cnt[s[i]]++;//将相同余数添加进入map容器
	}
	long long ans = 0;
	for(int i=0;i<k;i++)
	{
		ans+=(long long)cnt[i]*(cnt[i]-1)/2;//这里相同的余数进行两两的组合,那么肯定是组合数C(2,cnt[i])
	}
	cout<<ans<<endl;//输出结果
	return 0;
}

 

四【总结】

     当我们有一个一个想法的时候尽量要考虑到时间的优化,这里首先前缀和优化,后面有将内层for循环用排列组合公式优化,最后就得到了符合的代码。如有更好的代码,欢迎交流哦。谢谢

发布了123 篇原创文章 · 获赞 234 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_43919400/article/details/105323725
今日推荐