写在前面
- 题目要从易到难,遇到困难试着分解思考。
- 算法的复杂度(尽可能)控制在 O(N^2)内。
【1】2021-09-01 数组推导(100分)
因为单调不减的特性,最大值就是直接加;最小值就是单调栈求和。
#include<iostream>
using namespace std;
int main(){
int n , t , max_sum = 0 , min_sum = 0 , max_i = -1;
scanf("%d" , &n);
for(int i = 0 ; i < n ; i ++){
scanf("%d" , &t);
max_sum += t;
if(max_i < t){
max_i = t;
min_sum += t;
}
}
printf("%d\n%d" , max_sum , min_sum);
return 0;
}
【2】2021-04-01 灰度直方图(100分)
记一下 memset 和 freopen。
#include<iostream>
#include<string.h>
using namespace std;
int main(){
//freopen("Data.txt" , "r" , stdin);
int n , m , l , t , h[258];
memset(h , 0 , sizeof(h));
scanf("%d %d %d" , &n , &m , &l);
// 输入
for(int i = 0 ; i < n ; i ++){
for(int j = 0 ; j < m ; j ++){
scanf("%d" , &t);
h[t] += 1;
}
}
// 输出
for(int i = 0 ; i < l ; i ++){
if(i == l - 1) printf("%d" , h[i]);
else printf("%d " , h[i]);
}
return 0;
}
【3】2021-04-02 邻域均值(70分)
考察:【差分】
思路简单清晰,但是超时了。我当时就怀疑这种方法很好使,但是就是会超时。
有个优化的思路就是,对 Calculate 函数可以做一些小的改动就会变得更好。但是暂时我想不出来。
#include<iostream>
using namespace std;
const int N = 605;
int A[N][N] , r , t , n;
bool Calculate(int i , int j){
// 直接遍历循环
int sum = 0 , num = 0;
for(int a = i - r ; a <= i + r ; a ++){
for(int b = j - r ; b <= j + r ; b ++){
// 遇到边界值的时候跳过
if(a < 0 || a >= n || b < 0 || b >= n) continue;
// 求和并记录数据
sum += A[a][b];
num ++;
// 加速判断
if(sum > (2 * r + 1) * (2 * r + 1) * t) return false;
}
}
// 做出判断
if(sum <= num * t) return true; // black
else return false;
}
int main(){
//freopen("Data.txt" , "r" , stdin);
int L , sum = 0;
// 输入
scanf("%d %d %d %d" , &n , &L , &r , &t);
for(int i = 0 ; i < n ; i ++){
for(int j = 0 ; j < n ; j ++){
scanf("%d" , &A[i][j]);
}
}
// 计算 & 统计
for(int i = 0 ; i < n ; i ++){
for(int j = 0 ; j < n ; j ++){
if(Calculate(i , j) == true) sum ++;
}
}
// output
printf("%d" , sum);
return 0;
}
【4】2020-12-01 期末预测之安全指数(100分)
一个简单的求和运算。
#include<iostream>
using namespace std;
int main(){
int n , w , score , sum = 0;
scanf("%d" , &n);
for(int i = 0 ; i < n ; i ++){
scanf("%d %d" , &w , &score);
sum += w * score;
}
int y = max(sum , 0);
printf("%d" , y);
return 0;
}
【5】2020-12-02 期末预测之最佳阈值 (第一版:40分,第二版:100分)
40分的代码,不知道为什么只能拿到40分。首先一个感觉是这个循环可能有问题,时间复杂度有点高了;其他的暂时不明白。随后看看有没有什么高人给解释一下。
#include<iostream>
using namespace std;
int main(){
//freopen("Data.txt","r",stdin);
int m , max_theta = 0 , max_n = -1;
int w[100005] , result[100005];
//输入
scanf("%d" , &m);
for(int i = 0; i < m; i ++){
scanf("%d %d", &w[i] , &result[i]);
}
//如果成绩比pre要 ≥的都为及格1;成绩比pre低的要为 0
for(int i = 0 ; i < m ; i ++){
int pre = w[i] , n = 0; //n代表判断对的个数
for(int j = 0 ; j < m ; j ++){
if((pre <= w[j] && result[j] == 1) || (pre > w[j] && result[j] == 0))
n ++;
}
if(n >= max_n && pre >= max_theta){
max_n = n;
max_theta = pre;
}
}
printf("%d" , max_theta);
return 0;
}
第二版:时间复杂度从On2降为了On。
#include<iostream>
#include<set>
#include<algorithm>
#include<map>
using namespace std;
set<int> s;
map<int , int> Mzero;
map<int , int> Mone;
int main(){
int m;
int zero = 0 , one = 0;
//freopen("Data.txt" , "r" , stdin);
//input
scanf("%d" , &m);
for(int i = 0 ; i < m ; i ++){
int y , result;
scanf("%d %d" , &y , &result);
// 将所有的阈值都输入进来,从小到大做一个排序
s.insert(y);
//统计一下里面有多少个 0 和 1
if(result == 1) Mone[y] ++ , one ++;
else Mzero[y] ++ , zero ++;
}
// Find max
int Max = -1 , now_one = 0 , before_zero = zero , max_theta = -1;
for(set<int>:: iterator it = (--s.end()) ; it != (--s.begin()) ; it --){
now_one += Mone[*it];
before_zero -= Mzero[*it];
int max_i = now_one + before_zero;
// judge
if(max_i > Max){
Max = max_i;
if(max_theta == -1) max_theta = *it;
else if(max_theta > *it && max_theta != -1) max_theta = *it;
}
}
// output
printf("%d" , max_theta);
return 0;
}
【6】2020-09-01 称检测点查询 (100分)
pair的用法,可以结合sort使用。
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
//freopen("Data.txt","r",stdin);
int n , x , y;
pair<int , int> p[205];
scanf("%d %d %d" , &n , &x , &y);
for(int i = 1 ; i <= n ; i ++){
int a , b;
scanf("%d %d" , &a , &b);
int dis = (x - a)*(x - a) + (y - b)*(y - b);
p[i] = {dis , i};
}
sort(p , p + n); //对p从小到大排序
for(int i = 1 ; i <= 3 ; i ++){
printf("%d\n" , p[i].second);
}
return 0;
}
【7】2020-09-02 风险人群筛查 (100分)
主要考察的就是 flag 设计。
#include<iostream>
using namespace std;
int main(){
int n , k , t , xl , yd , xr , yu;
int bdfn = 0 , idfn = 0; // 经过人数,逗留人数
// freopen("data.txt" , "r" , stdin);
scanf("%d %d %d %d %d %d %d" , &n , &k , &t , &xl , &yd , &xr , &yu);
// 输入
for(int i = 1 ; i <= n ; i ++){
int num_k = 0;
bool is_dangerous = false , flag = false , dangerous = false;
for(int j = 1 ; j <= t ; j ++){
int x , y;
scanf("%d %d" , &x , &y);
//进入过
if((xl <= x && x <= xr) && (yd <= y && y <= yu))
is_dangerous = true , flag = true;
else
flag = false , num_k = 0;
//逗留
if(is_dangerous == true && flag == true){
num_k ++;
if(num_k >= k) dangerous = true;
}
}
// 统计个数
if(is_dangerous == true) bdfn ++;
if(dangerous == true) idfn ++;
}
// 输出
printf("%d\n%d" , bdfn , idfn);
return 0;
}
【8】2020-06-01 线性分类器 (100分)
思路
- 难点在于判断上。因为不知道上下哪个部分为A,哪个为B,故假设线性分类器上半部分为A,下半部分为B。统计点的数量。如果正确的话,这个点的数量要么是=n(如假设一致)要么等于0(如假设相反)。
- 双循环内部,j 和 i 不要写错了。否则很难查出错误。
#include<iostream>
using namespace std;
struct node{
int x , y;
char type;
}Node[100005];
//Function :Check
bool Check(int n , int t1 , int t2 , int t3){
int st = 0;//用于统计的量st
for(int i = 0 ; i < n ; i ++){
int key = t1 + Node[i].x * t2 + Node[i].y * t3;
//假设上半部分的数值为A,下半部分的数值为B。每个点带入,进行统计
//那么最终的答案要么是0(和上面完全相反),要么答案是n完全满足上方假设
if((key > 0 && Node[i].type == 'A') || (key < 0 && Node[i].type == 'B')) st ++;
}
if(st == 0 || st == n) return true;
else return false;
}
int main(){
int n , m;
freopen("Data.txt" , "r" , stdin);
scanf("%d %d" , &n , &m);
for(int i = 0 ; i < n ; i ++){
cin >> Node[i].x >> Node[i].y >> Node[i].type;
}
for(int i = 0 ; i < m ; i ++){
int theta1 , theta2 , theta3;
cin >> theta1 >> theta2 >> theta3;
bool ans = Check(n , theta1 , theta2 , theta3);
if(ans == 1) cout << "Yes" << endl;
else cout << "No" << endl;
}
return 0;
}
【9】2020-06-02 稀疏向量 (100分)
要点
- 我本来觉得这个题目开一个数组,因为太浪费空间了,所以选择用了一个 map。(map支持long long)
- 主体思路就是,将第一次的存好,第二次的如果一致就修改数据之后直接求和,不需要再开一个 for 循环等着求和了,那样会将第一次求和的都算进来。也不用 iterator 了直接求和就行。
- 当数字出现乘法的时候,要考虑是否会出现 int 不够的情况。需要用 long long(10的10次~18次,%lld )。
- map 可以直接赋值;map 的 find 函数和判断是重点,一定要会用。
#include<iostream>
#include<map>
using namespace std;
int main(){
int n , a , b , x;
long long y , sum = 0;
map<int , long long> mp;//直接用数组存也是可以的,但是那样数据太大了。
//freopen("Data.txt" , "r" , stdin);
scanf("%d %d %d" , &n , &a , &b);
for(int i = 0 ; i < a ; i ++){
scanf("%d %lld" , &x , &y);
//因为这是第一次,所以可以直接赋值
mp[x] = y;
}
for(int i = 0 ; i < b ; i ++){
scanf("%d %lld" , &x , &y);
//这是第二次的,因为这个map有值,则直接乘进去;没值就不用管了,反正内交一下也是0
if(mp.find(x) != mp.end()) mp[x] *= y;
//也不用遍历了,直接在这里求和
sum += mp[x];
}
//输出
cout << sum;
return 0;
}
【10】2019-12-01 报数(100分)
要点
- 注意这个输出。它是1,2,3,0这个顺序。
- 整体思路并不复杂,忘了审题了。7的倍数且要含有7。要有抽象的思维,把判断的函数抽出来,写出框架后再去判断。
#include<iostream>
using namespace std;
//Check function
bool Check(int i){
//若为7的倍数
if(i % 7 == 0) return true;
//若其中含有 7
while(i > 0){
if(i % 10 == 7) return true;
i /= 10;
}
return false;
}
int main(){
int n , st[5] = {0} , i = 1 , num = 0;
cin >> n;
while(1){
//统计数字
if(Check(i) == true) st[i % 4] ++ ;
else num ++;
//i也累加
i ++;
//跳出
if(num == n) break;
}
//输出
for(int i = 1 ; i <= 3 ; i ++)
cout << st[i] << endl;
cout << st[0] << endl;
return 0;
}
【11】2019-12-02 回收站选址(100分)
要点
不要试图将数据存入二维数组中,直接拿出来遍历循环即可。
#include<iostream>
using namespace std;
void stat_number(int x[] , int y[] , int n){
int stat[5] = {0};
// 寻找数据
for(int i = 1 ; i <= n ; i ++){
int v_num = 0 , c_num = 0;
for(int j = 1 ; j <= n ; j ++){
if((x[j] + 1 == x[i] && y[j] == y[i]) || (x[j] - 1 == x[i] && y[j] == y[i]) || (x[j] == x[i] && y[j] + 1 == y[i]) || (x[j] == x[i] && y[j] - 1 == y[i])) v_num ++; // 统计四边的个数
if((x[j] + 1 == x[i] && y[j] + 1 == y[i]) || (x[j] - 1 == x[i] && y[j] + 1 == y[i]) || (x[j] - 1 == x[i] && y[j] - 1 == y[i]) || (x[j] + 1 == x[i] && y[j] - 1 == y[i])) c_num ++; // 统计四角的个数
}
if(v_num == 4) stat[c_num] ++; // 满足的数据做统计
}
// 输出
printf("%d\n%d\n%d\n%d\n%d" , stat[0] , stat[1] , stat[2] , stat[3] , stat[4]);
}
int main(){
int n , x[1005] , y[1005];
// 输入
//freopen("data.txt" , "r" , stdin);
scanf("%d" , &n);
for(int i = 1 ; i <= n ; i ++){
scanf("%d %d" , &x[i] , &y[i]);
}
// 做统计
stat_number(x , y , n);
return 0;
}
【12】2019-09-01 小明种苹果(100分)
要点
- 最后的逻辑判断把我卡住了,一时半会儿居然没写出来,最后捋清楚思路重写了才写出来。
- 最后整理一下最后的那个思路:如果说两个数一致,且该数比最小的数要大,则不动;否则则赋值。
#include<iostream>
using namespace std;
int main(){
int N , M , min_i = 1005 , max_down_number = 0 , sum_number = 0;
int record[1005] = {0} , apple[1005] = {0};
// 输入
// freopen("Data.txt" , "r" , stdin);
scanf("%d %d" , &N , &M);
for(int i = 1 ; i <= N ; i ++){
// apple_number 记录了这个苹果树一开始的数量, down_number 记录了苹果树落下的个数;
int apple_number = 0 , d , down_number = 0;
for(int j = 0 ; j <= M ; j ++){
scanf("%d" , &d);
if(j == 0) apple_number = d;
else down_number += d;
}
// 计算最终的苹果数量;并做出统计
apple_number += down_number;
sum_number += apple_number;
// 做出判断,这里记得 down_number 都是负数
if(down_number <= max_down_number){
if(down_number == max_down_number && i >= min_i) ;//什么都不做
else min_i = i;
max_down_number = down_number;
}
}
// 输出
printf("%d %d %d" , sum_number , min_i , -1 * max_down_number);
return 0;
}
【13】2019-09-02 小明种苹果(续)(100分)
要点
- 重点是:看清楚让求得是啥。
- scanf 这个地方也值得记一下。
#include<iostream>
using namespace std;
// 利用 bool 数组和取模求三个连续的数
int tcn(bool drop[] , int n){
int drop_tree_number = 0;
for(int i = 0 ; i < n ; i ++){
if(drop[i % n] == true && drop[(i + 1) % n] == true && drop[(i + 2) % n] == true){
drop_tree_number ++;
}
}
return drop_tree_number;
}
int main(){
int n , m , drop_apple_number = 0 , sum_apple_number = 0;
bool is_apple_drop[1005] = {false};
// 输入
//freopen("Data.txt" , "r" , stdin);
scanf("%d" , &n);
for(int i = 0 ; i < n ; i ++){
int a , a_sum = 0;
scanf("%d" , &m);
for(int j = 1 ; j <= m ; j ++){
int apple;
scanf("%d" , &apple);
// 第一个数是最开始的苹果个数
if(j == 1) a_sum = apple;
// 中间间断的苹果个数
if(apple > 0) a = apple;
else{
a += apple;
a_sum += apple;
}
}
// 统计剩下的苹果总数
sum_apple_number += a;
// 判断是否有苹果掉落
if(a_sum - a){
drop_apple_number ++;
is_apple_drop[i] = true;
}
}
// 计算连续掉落的苹果树的个数
int three_con_number = tcn(is_apple_drop , n);
// 输出
printf("%d %d %d" , sum_apple_number , drop_apple_number , three_con_number);
return 0;
}
【14】2019-03-01 小中大(100分)
要点
重点是输出的地方的操作,需要按照需求做出输出。
#include<iostream>
using namespace std;
int main(){
int n , max_n , min_n , a , mid_n = 0;
bool dou = false;
//freopen("Data.txt" , "r" , stdin);
scanf("%d" , &n);
// 判断 n 是否是偶数
if(n % 2 == 0) dou = true;
for(int i = 1 ; i <= n ; i ++){
scanf("%d" , &a);
// 记录
if(i == 1) max_n = a;
if(i == n) min_n = a;
// 如果是数据是偶数的话,中位数先加起来,先不要判断
if(dou == true){
if(i == n / 2 || i == (n / 2 + 1)) mid_n += a;
}
else
if(i == n / 2 + 1) mid_n = a;
}
// 判断 , 最大值和最小值
if(min_n > max_n){
a = min_n;
min_n = max_n;
max_n = a;
}
// 判断 mid 是否可以整除 2,不能整除要保留1位小数,从大到小输出
if(dou == true){
if(mid_n % 2 == 0)
printf("%d %d %d" , max_n , mid_n / 2, min_n);
else
printf("%d %.1f %d" , max_n , float(mid_n) / 2 , min_n);
}
else
printf("%d %d %d" , max_n , mid_n , min_n);
return 0;
}
【15】2018-12-01 小明上学(100分)
要点
无,简单题。
#include<iostream>
using namespace std;
int main(){
int r , y , g , n , sum_t = 0;
// freopen("Data.txt" , "r" , stdin);
scanf("%d %d %d" , &r , &y , &g);
scanf("%d" , &n);
for(int i = 0 ; i < n ; i ++){
int k , t;
scanf("%d %d" , &k , &t);
if(k == 0){
sum_t += t;
}
else if(k == 1){
// if light is red
sum_t += t;
}
else if(k == 2){
// if light is yellow
sum_t += t + r;
}
}
// output
printf("%d" , sum_t);
return 0;
}
【16】2018-12-02 小明放学(60分)
注意
- freopen 打开位置的必须和 cpp 文件保持一致。
- 60 分的原因没有找到,有的说是 long long 的问题,有的其他问题考虑的不全,我也不太清楚具体的原因是什么。
#include<iostream>
using namespace std;
int get_sum(int r , int y , int g , int k , int t , int sum){
// 如果是路的话
if(k == 0) return t;
else if(k == 1){
// 当前是红色,计算当前所花费的时间,判断当前的灯应该是什么
int now = (r - t + sum) % (r + g + y);
// 判断当前时间是哪个节点:红色,绿色,黄色
if(now <= r) return (r - now);
if(now > r && now <= r + g) return 0;
if(now > r + g && now <= r + g + y) return (r + g + y - now + r);
}
else if(k == 2){
// 当前是黄色灯
int now = (r + g + y - t + sum) % (r + g + y);
// 判断当前时间是哪个节点:红色,绿色,黄色
if(now <= r) return (r - now);
if(now > r && now <= r + g) return 0;
if(now > r + g && now <= r + g + y) return (r + g + y - now + r);
}
else if(k == 3){
//当前是绿色灯
int now = (r + g - t + sum) % (r + g + y);
//判断
if(now <= r) return (r - now);
if(now > r && now <= r + g) return 0;
if(now > r + g && now <= r + g + y) return (r + g + y - now + r);
}
}
int main(){
int r , y , g , n , sum = 0;
// freopen("Data.txt" , "r" , stdin);
// input
scanf("%d %d %d" , &r , &y , &g);
scanf("%d" , &n);
for(int i = 0 ; i < n ; i ++){
int k , t;
scanf("%d %d" , &k , &t);
// 计算每一步需要的时间
int gt = get_sum(r , y , g , k , t , sum);
sum += gt;
}
// 输出
printf("%d" , sum);
return 0;
}
【17】2018-09-01 卖菜(100分)
注意这个地方需要重新开一个数组b。
#include<iostream>
using namespace std;
int main(){
int n , a[1005] , b[1005];
// freopen("Data.txt" , "r" , stdin);
// 输入
scanf("%d" , &n);
for(int i = 1 ; i <= n ; i ++){
scanf("%d" , &a[i]);
}
// 调整
for(int i = 1 ; i <= n ; i ++){
//针对头尾调整
if(i == 1) b[i] = (a[i] + a[i + 1]) / 2;
else if(i == n) b[i] = (a[i] + a[i - 1]) / 2;
else b[i] = (a[i - 1] + a[i] + a[i + 1]) / 3;
}
//输出
for(int i = 1 ; i <= n ; i ++){
if(i == n) printf("%d" , b[i]);
else printf("%d " , b[i]);
}
return 0;
}
【18】2018-09-02 买菜(100分)
注意:不难,画个图就看明白了。开一个大数组,然后往里填数数数就可以。
#include<iostream>
using namespace std;
int time[1000005] = {0};
int main(){
int n;
//freopen("Data.txt" ,"r" , stdin);
scanf("%d" , &n);
for(int i = 0 ; i < n ; i ++){
int a , b;
scanf("%d %d" , &a ,&b);
// 将H标记好的数据放进去
for(int j = a ; j < b ; j ++){
time[j] = 1;
}
}
// 设计一个求和 sum
int sum = 0;
for(int i = 0 ; i < n ; i ++){
int a , b;
scanf("%d %d" , &a , &b);
// 在小 W 的时间段内,小H也有数据做过标记,则直接 sum ++;
for(int j = a ; j < b ; j ++){
if(time[j] == 1) sum ++;
}
}
printf("%d" , sum);
return 0;
}
【19】2018-03-01 跳一跳(100分)
做好标记。
#include<iostream>
using namespace std;
int time[1000005] = {0};
int main(){
int n ;
long long sum = 0;
int cont = 1; // 连续标记
// freopen("Data.txt" ,"r" , stdin);
while(scanf("%d" , &n) == 1){
if(n == 1) {
sum += 1;
cont = 1;
}
else if(n == 2){
sum += 2 * cont; // 对于连续的做出统计,做和
cont ++;
}
else if(n == 0) break; // 退出
}
printf("%lld" , sum);
return 0;
}
【20】2018-03-02 碰撞的小球
没做完。
#include<iostream>
using namespace std;
int ball[105];
int main(){
int n , L , t;
scanf("%d %d %d" , &n , &L , &t);
for(int i = 1 ; i <= n ; i ++){
scanf("%d" , &ball[i]);
}
// 移动球
for(int i = 1 ; i <= t ; i ++){
for(int j = n ; j >= 1 ; j --){
if(ball[j] < L) ball[j] += 1;
else if(ball[j] >= L){
ball[j] -= 1;
L = ball[j];
}
}
for(int j = 1 ; j <= n ; j ++){
printf("%d " , ball[j]);
}
printf("\n");
}
// output
for(int i = 1 ; i <= n ; i ++){
if(i == n) printf("%d" , ball[i]);
else printf("%d " , ball[i]);
}
return 0;
}
【21】2016-12-01 中间数(100分)
注意
- 复习一下 sort 的用法
- 不用开两个指针,只要总数 - 前面 - 中间重复数字就可以。
#include<iostream>
#include<algorithm>
using namespace std;
bool cmp(int a , int b){
return a < b;
}
int main(){
int n , a , num[1005], stat[1005] = { 0 } , state_num = 0 , j = 0;
//freopen("Data.txt" , "r" , stdin);
scanf("%d" , &n);
for(int i = 0 ; i < n ; i ++){
scanf("%d" , &a);
stat[a] += 1;
if(stat[a] <= 1){
num[j ++] = a ;
state_num ++;
}
}
// 对数组排序
sort(num , num + state_num , cmp);
// output
for(int i = 0 ; i < state_num ; i ++){
int sum = 0;
for(int j = 0 ; j < i ; j ++){
if(num[j] < num[i]) sum += stat[num[j]];
else break;
}
//判断中间数是否存在
if(sum == n - sum - stat[num[i]]){
printf("%d" , num[i]);
return 0;
}
}
printf("-1");
return 0;
}