语言:C语言 由于clion会自动加上return,所以有时候可能没写……
CF44C Holidays
题目描述:
n天假期,安排m个人来浇花,第i个人负责[a[i],b[i]]天,问花是否可以每天都被浇水且不重复。 可以的话输出“OK”,不可以的话输出最早出问题的那天的天号以及那天花被浇了多少次水。
1≤n,m≤100 1≤a[i]≤b[i]≤n b[i]≤a[i+1] (这个意思就是下一行的第一个数不小于上一行的第二个数)
题解如下:
//
// Created by Lee on 2022/1/25.
//main10
//
#include <stdio.h>
int main(){
int n = 0;//n天假期
int m = 0;//m个人
int a[200] = {0};
int b[200] = {0};
int rec[200] ={0};//每一天浇水次数,下标为日期
scanf("%d %d",&n,&m);
scanf("%d %d",a,b);
if(a[0]!=1){
printf("1 0");
return 0;
} else{
for(int i=a[0];i<=b[0];i++){
rec[i]++;
}
for(int i=1;i<m;i++){//构造rec数组
scanf("%d %d",a+i,b+i);
for(int j=a[i];j<=b[i];j++){
rec[j]++;
}
}
for(int i=1;i<=n;i++){//遍历rec数组,看有无多浇水或漏浇水的情况
if(rec[i]==0) {
printf("%d 0",i);
return 0;
}
if(rec[i]>1) {
printf("%d %d",i,rec[i]);
return 0;
}
}
printf("OK");
}
return 0;
}
本题需要注意的地方:
1. 思路:这道题可以用差分或者模拟,我当时没学差分,而且模拟不会超时,我就直接用了模拟。直接开一个数组记录每一天的浇水次数。(后面有用差分的题P2367 语文成绩)
P2367 语文成绩
题目描述:
语文老师总是写错成绩,所以当她修改成绩的时候,总是累得不行。她总是要一遍遍地给某些同学增加分数,又要注意最低分是多少。你能帮帮她吗?
输入格式:
第一行有两个整数 n,p,代表学生数与增加分数的次数。
第二行有 n 个数, a1∼an,代表各个学生的初始成绩。
接下来 p 行,每行有三个数,x,y,z,代表给第 x 个到第 y 个学生每人增加 z 分。
输出格式:
输出仅一行,代表更改分数后,全班的最低分。
题解如下:
//
// Created by Lee on 2022/1/26.
//main12
//暴力时间复杂度太高,需要用差分
//
#include <stdio.h>
int a[5000100] = {0};
int b[5000100] = {0};//差分数组
int main(){
int n = 0;//学生数
int p = 0;//操作加分次数
scanf("%d %d ",&n,&p);
for(int i=1;i<=n;i++){
scanf("%d",a+i);//初始化同学们的分数
b[i] = a[i] - a[i-1];
}
int x=0,y=0,z=0;//从第x到第y个学生,每人增加z分
for(int i=0;i<p;i++){
scanf("%d %d %d",&x,&y,&z);
b[x] += z;
b[y+1] -= z;
}
for(int i=1;i<=n;i++){//把b数组求前n项和,还原a数组
a[i] = a[i-1] + b[i];
}
int min = a[1];
for(int i=1;i<=n;i++){//遍历找最低
if(a[i]<min){
min = a[i];
}
}
printf("%d",min);
}
本题需要注意的地方:
1. 首先是数组容量的分配。题中所给范围为n≤5e6,p≤n。我之前以为是五十万,然后一直过不了,最后发现是五百万……所以建议直接定义一个const int MAX = 5e6+10(一般加一点点),分配数组时直接int array[MAX]。不过纯C好像不支持这种写法,你可以把文件后缀由.c改成.cpp。一般都会有cpp编译器吧。
2. 算法:本题使用了差分数组,顾名思义就是用数组b来表示原数组a,递推公式如下
差分数组学习参考链接:前缀和 & 差分 - OI Wiki
3. 得到差分数组后,用前缀和还原。再遍历数组后,可得到最低分。
P1160
题目描述:
一个学校里老师要将班上N个同学排成一列,同学被编号为1∼N,他采取如下的方法:
-
先将1号同学安排进队列,这时队列中只有他一个人;
-
2−N号同学依次入列,编号为i的同学入列方式为:老师指定编号为i的同学站在编号为1∼(i−1)中某位同学(即之前已经入列的同学)的左边或右边;
-
从队列中去掉M(M<N)个同学,其他同学位置顺序不变。
在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。
题解如下:
//
// Created by Lee on 2022/1/26.
//main11
//
//输入格式
//第1行为一个正整数N,表示了有N个同学。
//第2-N行,第i行包含两个整数k,p,其中k为小于i的正整数,p为0或者1。若p为0,则表示将i号同学插入到k号同学的左边,p为1则表示插入到右边。
//第N+1行为一个正整数M,表示去掉的同学数目。
//接下来M行,每行一个正整数x,表示将x号同学从队列中移去,如果x号同学已经不在队列中则忽略这一条指令。
//输出格式
//1行,包含最多N个空格隔开的正整数,表示了队列从左到右所有同学的编号,行末换行且无空格。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
int N = 0;//有N个同学
int M = 0;//去掉M个同学
int l = 0;//记录谁是第一个同学,即最左边left
scanf("%d",&N);
int a[100010][2] = {0};//记录N个同学,左边和右边的人是谁,0代表左边的人,1代表右边的人。
int rec[100010] = {0};//记录i同学是否在队列中,1表示在,0表示已经被删除过了
l = 1;
rec[1] = 1;
int k=0,p=0;
for(int i=2;i<=N;i++){//把序号为i的同学,插在序号为k的同学的p边
scanf("%d %d",&k,&p);
rec[i] = 1;
if(p==0){//放在k的左边
a[i][0] = a[k][0];
a[i][1] = k;
a[a[k][0]][1] = i;
a[k][0] = i;
if(k==l){//若k是最左边的,则把最左边的置为i
l = i;
}
}
else{//放在k的右边
a[i][0] = k;
a[i][1] = a[k][1];
a[a[k][1]][0] = i;
a[k][1] = i;
}
}
scanf("%d",&M);
int x = 0;//删除x号同学
for(int i=1;i<=M;i++){
scanf("%d",&x);
if(rec[x]==1){
a[a[x][0]][1] = a[x][1];
a[a[x][1]][0] = a[x][0];
rec[x] = 0;
} else{
continue;
}
}
int next = l;
printf("%d ",l);
do {
printf("%d ",a[next][1]);
next = a[next][1];
}while(a[next][1]!=0);
}
/*可用于自测的输入数据
5
1 1
2 1
3 1
4 1
5
3
2
3
4
3
*/
本题需要注意的地方:
1. 算法:用数组模拟双向链表。
2. 添加同学时,注意链表箭头更新的顺序,不要丢失指向。
3. 删除同学时,注意我们之前有个标记数组,标记某个同学是否被删除过。如果重复删除同学,而且有隔壁同学也被删了的话,可能会把已经删去的箭头重新链上。