要写第二篇不知道写啥,又只能来水一道编程题了(等周末的时候总结一下自己这几天学过的内容,好好写一篇)
题目描述:
C市现在要转移一批罪犯到D市,C市有n名罪犯,按照入狱时间有顺序,另外每个罪犯有一个罪行值,值越大罪越重。现在为了方便管理,市长决定转移入狱时间连续的c名犯人,同时要求转移犯人的罪行值之和不超过t,问有多少种选择的方式? (题目链接:
点击打开链接)
输入描述:
第一行数据三个整数:n,t,c(1≤n≤2e5,0≤t≤1e9,1≤c≤n),第二行按入狱时间给出每个犯人的罪行值ai(0≤ai≤1e9)
输出描述:
一行输出答案。
示例1
输入
3 100 2 1 2 3
输出
2
题目分析
刚开始看了好一会,才把题看懂,中午没睡好,精神有点恍惚。
简单来说就是给你一个数组,让你求有多少个长度为 C 的连续子数组的和小于等于题目给定的数字 T。(为了看的清楚一些,容易看花的英文字母都使用了大写)
然后这道题就简单了,我一开始想的来两个循环外层的控制次数,从N个数中选出连续的C个一共有N-C+1种,故循环变量为[ 0 , n - c ],
然后内层循环中定义一个变量sum,从数组的第 i 个元素加到 第i + C - 1 个, 之后再判断sum是否<=T。(感觉这里有个陷阱,题目上说每个犯人的犯罪值在 0 到 1e9之间,
如果把sum定义为int类型可能会导致溢出,使得本来大于 T 的 sum 变得小于了 T , 导致计算结果出错, 所以为了保险起见我定义成了long long int。)
#include <iostream>
using namespace std;
int main() {
int n, t, c;
while (cin >> n >> t >> c) {
int *crime_value = new int[n];
for (int i = 0; i < n; ++i) cin >> crime_value[i];
int method = 0;
long long int sum = 0;
for (int i = 0; i <= n - c; ++i) {
for (int j = i; j < i + c; ++j) {
sum += crime_value[j];
}
if (sum <= t) ++method;
}
cout << method << endl;
delete[] crime_value;
}
return 0;
}
所以,我就提交了这样的代码,结果超时了......很显然,这道题是想让我们降低时间复杂度
在一番思考过后,我发现当求出第一个长度为 C 的连续子数组的和之后,对于后面的连续子数组的和并不需要全部重新计算,
例如第二个连续子数组的和可以由第一个的和减去原数组中的第一个数字,然后再加上第 C 个数字就是其和。
于是,经过简单的修改过后不难得出时间复杂度为常数的代码。
最终代码
#include <iostream>
using namespace std;
int main() {
int n, t, c;
while (cin >> n >> t >> c) {
int *crime_value = new int[n];
for (int i = 0; i < n; ++i) cin >> crime_value[i];
int method = 0;
long long int sum = 0;
for (int i = 0; i <= n - c; ++i) {
if (0 == i) { // 只需计算一次连续子数组的和
for (int j = i; j < i + c; ++j) {
sum += crime_value[j];
}
} else {
sum = sum - crime_value[i - 1] + crime_value[i - 1 + c];
}
if (sum <= t) ++method;
}
cout << method << endl;
delete[] crime_value;
}
return 0;
}