2017蓝桥杯真题 k倍区间(暴力+优化)-CSDN博客

53 阅读1分钟

 

本题乍一看好像并不会很难,思路也比较容易想到用暴力解决这道题(毕竟只是连续子序列)~~

#include<stdio.h>
int main()
{
	int num[100005],i,j,n,k,sum=0,cn=0;
	scanf("%d%d",&n,&k);
	for(i=0;i<n;i++)
	{
		scanf("%d",&num[i]);
	}
	for(i=0;i<n;i++)
	{
		sum=num[i];
		for(j=i+1;j<n;j++)
		{
			sum+=num[j];
			if(sum%k==0)
			{
				cn++;
			}
		}
	}
	for(i=0;i<n;i++)
	{
		if(num[i]%k==0)
		{
			cn++;
		}
	}
	printf("%d",cn);
	return 0;
 } 

我也是直接用for循环暴力试了一下发现超时了,一般如果超时了,我们就必须优化我们的算法了

奈何学识浅薄,我未能想到如何优化,最后求助于老师才有了下面的两个算法!!!!!!!!

首先是前缀法!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

前缀法是区间问题优化的一个“利器”

#include<stdio.h>
int num[100005],sum[100005];
int main()
{
	int n,t,i,j,cn=0;
	scanf("%d%d",&n,&t);
	num[0]=0;
	for(i=1;i<=n;i++)
	{
		scanf("%d",&num[i]);
		sum[i]=sum[i-1]+num[i];
	}
	
	for(i=1;i<=n;i++)
	{
		for(j=i;j<=n;j++)
		{
			if((sum[j]-sum[i-1])%t==0)
			{
				cn++;
			}
		}
	}
	printf("%d",cn);
	return 0;
 } 

这个方法主要我们通过构建另外一个数组来存放Si 即前 i 项和,再通过Sj - Si-1 来减少运算量!!

下面还有另外一个方法(需要用到一点数学知识,较难想到)

#include<stdio.h>
int main()
{
	
	int i,n,k,a[100005],s[100005],cnt[100];
	scanf("%d%d",&n,&k);
	s[0]=0;
	cnt[0]=1;
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		s[i]=(s[i-1]+a[i])%k;
		cnt[s[i]]++;
		
	}
	long long ans=0;
	for(i=0;i<k;++i)
	{
		ans+=(long long)cnt[i]*(cnt[i]-1)/2;
	}
	printf("%lld",ans);
	return 0;
}

首先给出这个数学结论,即————两个数(a,b)与另外一个数(c)取模后得到相等的一个数(d)时,这两个数(a,b)的差一定是c的倍数————这样的两个数区间即k倍区间!!!!!

那么我们通过这个结论,我们便可以无需使用多重循环去枚举了

而是直接在输入数据的时候求和后直接取模,最后利用组合数进行计算即可

详情请看代码~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~