第八届蓝桥杯第十题–K倍区间(C语言)
一.比赛题目
1.题目要求
给定一个长度为N的数列,A1, A2, … AN,如果其中一段连续的子序列Ai, Ai+1, … Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。 你能求出数列中总共有多少个K倍区间吗?
2.输入与输出
输入:
第一行包含两个整数N和K。(1 <= N, K <= 100000) 以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)
输出:
输出一个整数,代表K倍区间的数目。
样例输入:
5 2
1
2
3
4
5
样例输出:
6
二.分析过程
1.总体分析
这道题一眼就能看懂,双重for循环,一个i=0到n,一个j=i+1到n,就可以把这个结果求出来;但是就这道题而言,1 <= N, K <= 100000,所以肯定会超时。问题就在于如何节省时间;
在前段时间学习了动规,背包之后,有了填表,拆分成子问题来解决的一种思想。
2.问题分析
就样例输入而言,可以把1-5来进行填表:
序号 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
数字 | 1 | 2 | 3 | 4 | 5 |
总和 | 1 | 3 | 6 | 10 | 15 |
因为第一个数字需要考虑1,1+2,1+2+3,1+2+3+4等情况,而最后一个数字仅需要考虑5他本身,所以我们采用逆推的想法来填表:
序号 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
数字 | 1 | 2 | 3 | 4 | 5 |
递减总和 | 15 | 14 | 12 | 9 | 5 |
我们得到:第i个递减总和–第i+3个递减总和,就是第i个–第i+2个数字的连续区间之和
例如第二个数字的递减总和–第五个数字的递减总和=第二个到第四个连续区间相加的总和;
第二个数字的递减总和–第四个数字的递减总和=第二个到第三个连续区间相加的总和;
第n个数字:
第n-1个数字:
注:不满足条件时是a[n-1]-a[n],即为倒数第二个;
…
通过递推,我们发现了第i个开始的连续区间可以用这种方式来表示,所以就能够节约下来双重for循环的时间;
代码如下:
for(int i=n-1;i>=0;i--)
{
if(c[i]%k==0)
sum++;
for(int j=n-1;j>i;j--)
{
if((c[i]-c[j])%k==0)
sum++;
}
}
//c[i]就是表里面递推的总和;
三.整体代码
#include<stdio.h>
int main()
{
int n,k;
scanf("%d %d",&n,&k);
int sum=0;
//记录输入数据的总和;
int count=0;
//记录k倍区间的数目;
int a[n],c[n];
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
//输入数据且计算总和;
c[0]=sum;
//第一个数赋值为全部数的总和;
for(int i=1;i<n;i++)
{
c[i]=c[i-1]-a[i-1];
}
//递减总和;
for(int i=n-1;i>=0;i--)
{
if(c[i]%k==0)
count++;
for(int j=n-1;j>i;j--)
{
if((c[i]-c[j])%k==0)
count++;
}
}
//条件筛选;
printf("%d",count);
return 0;
}
//其实每次从后向前递推
//这样也能过的
#include<stdio.h>
int n,k,a[100010];
int main()
{
int num=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
int sum=0;
for(int j=i;j>=1;j--)
{
sum+=a[j];
if(sum%k==0)
num++;
}
}
printf("%d",num);
return 0;
}
四.总结
这道题是会了算法思想之后再来看的一道题,虽然没有用到,但是在解决问题的时候递推观察还是挺有帮助的。接下来就要学习其他的算法和做题了。