说在最开始的话
这篇博客仅仅是提供学习资料和我个人的复习使用,在学习算法的过程中代码还是要自己亲手打一遍,不要图方便直接复制粘贴代码。复制代码百害而无一利。
前言
写这个系列博客的初衷是学习算法的过程有些粗糙,对于各个算法的掌握也是不太熟练,再加上复习时苦于没有往年题目的痛苦。为了自己和学弟学妹在算法上能有所进步,开坑20级算法分析与设计。
本次更新C1 C1更多是对于数据结构和c语言基础的复习,涉及算法的部分不多,大家可以看一下自己的知识掌握如何。
A 计数排序
题目描述
插入排序的一个伪代码如下:
insert_sort(A):
for j = 1 to len(A):
key = A[j]
# insert A[j] to sorted sequence A[0...j-1]
i = j - 1
while i >= 0 and A[i] > key:
A[i + 1] = A[i]
i = i - 1
A[i + 1] = key
给定待排序的数组,请输出排序过程中,第 6 行和第 8 行的执行次数
输入
一行若干个整数,表示待排序的数组(n≤1000)
输出
两个整数,分别表示第六行和第八行的执行次数
分析
这个题目难度不大,实现伪代码后使用变量记录次数即可下面是我自己的代码
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
int a[1005];
int main()
{
int n ,i=0,j,key,count1=0,count2=0,num=0,str;
while (scanf("%d", &a[i])!=EOF) {
i++;
}
str = i;
for (j = 1; j < str; j++){
key=a[j];
i=j-1;
while (i >= 0 && a[i] > key) {
a[i+1]=a[i];
count1++;
i--,count2++;
}
count1++;
a[i+1]=key;
}
printf("%d %d", count1, count2);
return 0;
}
B 秦九韶
题目描述
秦九韶算法是一种将一元n次多项式的求值问题转化为n个一次式的算法。
其大大简化了计算过程,即使在现代,利用计算机解决多项式的求值问题时,秦九韶算法依然是最优的算法。
现在,给出一个n次多项式,请求出其在x=x0处的值。答案对998244353取模。
输入
第一行一个正整数n,表示多项式的最高次数。
接下来一行有n+1个正整数,用空格隔开,表示从高到低的对应项的系数。
第一个表示最高次的系数,最后一个表示常数项。
第三行一个整数,表示x0的值。
输出
输出这个多项式在x=x0处的值。
分析
这个题目依旧难度不大,只要理解了秦九绍算法的意思就不难解决
下面是我自己的代码
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<math.h>
int a[200005];
int main()
{
int n, i = 0, j, key, count1 = 0, count2 = 0, num = 0;
long long int res=0;
scanf("%d", &n);
for (i = n; i >=0; i--) {
scanf("%d", &a[i]);
}
scanf("%d", &num);
for (i = n; i >= 0; i--) {
//res=(((res*(num%998244353))%998244353)%998244353+a[i]%998244353);
res=res*num+a[i];
res=res%998244353;
}
printf("%lld",res);
return 0;
}
C 逆序对
题目描述
给定一个整数序列,求逆序对的个数。
输入
第一行一个整数,表示序列长度 n
接下来一行,n 个整数,保证在 int 范围内
输出
输出一行一个整数,表示逆序对的个数
输入样例
5
5 4 3 2 1
输出样例
10
分析
这个题目有些难度,需要用到mergesort二分算法。
对于逆序对的概念来说也不难想到二分算法,看具体代码可能更容易理解,
下面是我自己的代码。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<math.h>
int a[100005];
long long int count;
void megre(int a[],int p,int q,int r) {
int n1, n2, i, j, k;
n1 = q - p + 1;
n2 = r - q;
int L[50005];
int R[50005];
/*int *L = (int *)malloc((n1+5) * sizeof(int));
int *R = (int *)malloc((n2+5) * sizeof(int));*/
for (i = 1; i <= n1; i++)
L[i] = a[p + i - 1];
for (j = 1; j <= n2; j++)
R[j] = a[q + j];
L[i]=0x7fffffff;
R[j]=0x7fffffff;
i = 1, j = 1;
for (k = p; k <= r; k++) {
if (L[i] <= R[j]){
a[k] = L[i];
i++;
}
else{
a[k] = R[j];
count+=n1-i+1;
j++;
}
}
//free(L);
//free(R);
}
void megre_sort(int a[], int p,int r) {
int q;
if (p < r){
q = (p + r) / 2;
megre_sort(a, p, q);
megre_sort(a, q + 1, r);
megre(a, p, q, r);
}
}
int main()
{
int n, i = 0, j, key, num = 0;
scanf("%d", &n);
for (i = 1; i <= n; i++)
scanf("%d", &a[i]);
megre_sort(a,1,n);
printf("%lld",count);
return 0;
}
D Ashane算日期 (easy version)
题目描述
Ashane 喜欢用 8 位数字表示一个日期,其中前 4 位代表年,接下来 2 位代表月,最后 2 位代表日。
现在,他想知道指定两个日期之间(包含这两个日期本身),有多少个日期是幸运日期。
一个日期是幸运的当且仅当表示这个日期的 8 位数字是回文的。
输入
输入一共两行
每行一个 8 位数字,代表指定的两个日期(保证输入日期合法且年份 year 满足 1000≤year≤9999)。
输出
输出一行一个整数,表示指定两个日期之间幸运日期的个数。
输入样例
20210919
20220919
输出样例
1
分析
这个题目有些难度需要考虑的东西很多,我个人没有想出很简洁的算法
属于是用遍历判定的算法来判断每一个合法日期是否是回文日期。
下面是我自己的代码,仅供参考。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<math.h>
char time1[10];
char time2[10];
char temp[10];
int chackyear(){
int i,year1=0,year2=0;
for(i=0;i<4;i++)
year1=year1*10+time1[i]-'0';
for(i=0;i<4;i++)
year2=year2*10+time2[i]-'0';
if(year1<=year2)
return year1;
else{
strcpy(temp,time2);
strcpy(time2,time1);
strcpy(time1,temp);
return year2;
}
}
int chackmon(){
return ((time1[4]-'0')*10+time1[5]-'0');
}
int chackday(){
return ((time1[6]-'0')*10+time1[7]-'0');
}
int chackyear2(){
int year2=0,i;
for(i=0;i<4;i++)
year2=year2*10+time2[i]-'0';
return year2;
}
int chackmon2(){
return ((time2[4]-'0')*10+time2[5]-'0');
}
int chackday2(){
return ((time2[6]-'0')*10+time2[7]-'0');
}
int chack(){
int i;
if(temp[3]>1)
return 0;
for(i=0;i<4;i++){
if(temp[i]!=temp[7-i])
return 0;
}
return 1;
}
void ito(int time,char temp[]){
int i=0;
for(i=7;i>=0;i--){
temp[i]=time%10;
time=time/10;
}
}
int main()
{
int year,year2,mon,mon2,day,day2,count=0,time=0,type=0,pan=0;
int mon_day[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
scanf("%s",time1);
scanf("%s",time2);
year=chackyear();
mon=chackmon();
day=chackday();
year2=chackyear2();
mon2=chackmon2();
day2=chackday2();
if(year<1000||year2<1000)
pan=2;
while(year!=year2||mon!=mon2||day!=day2){
if(year>year2)
break;
if(year%10>1){
year=year/10;
year++;
year*=10;
mon=1;day=1;
continue;
}
time=year*10000+mon*100+day;
ito(time, temp);
mon_day[2]=28;
if(year%4==0)
mon_day[2]=29;
if(year%100==0)
mon_day[2]=28;
if(year%400==0)
mon_day[2]=29;
type=chack();
if(type==1)
count++;
day++;
if(day>mon_day[mon]){
day=1;
mon++;
}
if(mon>12){
mon=1;
year++;
}
}
time=year2*10000+mon2*100+day2;
ito(time, temp);
mon_day[2]=28;
if(year2%4==0)
mon_day[2]=29;
if(year2%100==0)
mon_day[2]=28;
if(year2%400==0)
mon_day[2]=29;
type=chack();
if(type==1)
count++;
if(pan==2)
return 0;
else
printf("%d",count);
return 0;
}
E 繁衍
题目描述
众所周知,兔子是一种繁殖能力极强的生物。据说在很久很久以前的澳大利亚是没有兔子的,直到有人带了一对兔子过去,兔子的踪迹便在数年之内遍布了整个澳大利亚大陆。
现在请你模拟一下当前的情况。假设第1年有2只刚刚出生的幼年兔子,它们会在出生a年后变得成熟。每两只成熟的 兔子每年会产一只幼崽(如果成年兔子的总数是奇数,则最后一只兔子不能配对,无法产仔)。
每只兔子的成熟期只有b年,在那之后它就会衰老,就不能再产下幼崽。我们截取的时间比较短,所以不考虑兔子死亡的情况。现在,请你算出,在第x年的时候,一共有多少只兔子。
注意,假如有2n只成熟的兔子,你可以认为他们一定可以配成n对并产下幼崽。这个题目中的模型比较理想化,你不需要考虑关于兔子性别的任何问题。。
输入
第一行三个正整数,用空格隔开,代表a,b,x。
输出
输出一个正整数,表示第x年有多少只兔子。
输入样例
1 1 5
输出样例
3
分析
这个题目难度不大,按照兔子的成熟,衰老,和个数进行模拟即可得出最后的答案
下面是我自己的代码,仅供参考。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<math.h>
int num;
int rab[15];
void rise(int a,int b,int year){
int i,cheng=0;
if(year==1){
rab[year]=2;
return;
}
else{
for(i=1;i<year;i++){
if((i+a)<=year&&(i+b+a)>year)
cheng+=rab[i];
}
rab[year]+=cheng/2;
}
}
int main()
{
int n, i = 0, a,b,x,year=1,count=0;
scanf("%d%d%d", &a,&b,&x);
while(year<=x){
rise(a,b,year);
year++;
}
for(i=1;i<=x;i++){
count+=rab[i];
}
printf("%d",count);
return 0;
}
F 黑子的训练
题目描述
BUAA 校级篮球联赛正如火如荼得进行着,教练在训练时对每个队员又提出了新的标准。
教练要求队员黑子在一次投篮训练中得到 **n** 分,黑子可以选择每次投两分球 **2** 分或三分球 **3** 分,
但由于两分球比较容易投进,教练要求黑子投射的两分球数量不超过 **max_two** 个。
请输出黑子有多少种不同的投球顺序,使得恰好完成了教练要求的得分(假设投的球都能命中)。
输入
一行,两个以空格分隔的正整数 n 、max_two( 2≤n≤70 ,2≤max_two≤8 ),含义如题目描述所述。
输出
一行,一个非负整数,表示有多少种不同的投球顺序。
输入样例
8 2
输出样例
3
分析
这个题目难度不大,是一道递归题目。实现起来不难直接放代码。
下面是我自己的代码,仅供参考。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<math.h>
int count;
void shoot(int n,int max){
if(n==0){
count++;
}
if(max>0&&n-2>=0)
shoot(n-2,max-1);
if(n-3>=0)
shoot(n-3,max);
return;
}
int main()
{
int n, max;
scanf("%d%d",&n,&max);
shoot(n,max);
printf("%d",count);
return 0;
}
G Matrix53的博弈
题目描述
Matrix53 和 Alex 都很喜欢下围棋。一天,在和 Alex 下围棋时,Matrix53 突然想到,如果能知道自己当前的胜率该多好啊,这样就可以知道应该激进还是稳健行棋了。
Matrix53 把棋局抽象成了一棵树,树的每个结点代表一个局面,树根代表初始局面。Matrix53 或者 Alex 行棋,相当于选择了棋局所在结点的某个子结点。
现在给定这棵树,以及棋局位于各个叶子结点时 Matrix53 的胜率,求棋局位于树根时 Matrix53 的胜率。
Matrix53 和 Alex 都会做出最优选择,且 Matrix53 先行棋。
输入
第一行为这棵树的结点总数 n
接下来 n-1 行,每行两个整数 a,b,表示结点 a 是结点 b 的父结点,且树根结点的编号为 1
最后一行有 k 个整数(k 为这棵树的叶子结点个数),每个整数 s 表示棋局位于一个叶子结点时 Matrix53 的胜率
这 k 个整数 s 按照叶子结点的编号从小到大的顺序给出,s 越大则 Matrix53 的胜率越高(可以认为 s 为 9550 时 Matrix53 的胜率为 95.50%,以此类推)
输出
输出一行,以整数表示的棋局位于树根时 Matrix53 的胜率
输入样例
3
1 2
1 3
3090 2060
输出样例
3090
分析
这个题目有一定难度。但并不是博弈论的题目。更多的是考察对于树的知识的掌握。在每次选择时两个人都会选择对自己最有利的方案。按照这个方式选取左右子节点即可。
下面是我自己的代码,仅供参考。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<math.h>
typedef struct node Node;
struct node{
int num;
int childnum;
int parent;
int win;
int leaf;
int deep;
int move;
int leaffor;
};
Node qi[1000005];
void setwin(int n){
int i,par;
if(qi[1].leaf==1&&qi[1].childnum==0)
return;
for(i=1;i<=n;i++)
if(qi[i].leaf==1&&qi[i].childnum==0){
par=qi[i].parent;
if(qi[par].deep%2==0){
if(qi[par].win>qi[i].win||qi[par].win==0){
qi[par].win=qi[i].win;
qi[par].move=i;
}
qi[par].childnum-=1;
}
else{
if(qi[par].win<qi[i].win||qi[par].win==0){
qi[par].win=qi[i].win;
qi[par].move=i;
}
qi[par].childnum-=1;
}
qi[i].leaf=-1;
qi[par].leaf=1;
}
setwin(n);
return;
}
void condeep(int n){
int i,par,type=1;
while(1){
for(i=1;i<=n;i++){
par=qi[i].parent;
if(qi[par].deep>0&&qi[i].deep==0){
qi[i].deep=qi[par].deep+1;
type=0;
}
}
if(type==1)
return;
else
type=1;
}
}
int main()
{
int n,a,b,k,i,win;
scanf("%d",&n);
for(i=1;i<=n;i++){
qi[i].childnum=0;
qi[i].leaf=-1;
qi[i].win=0;
qi[i].deep=0;
qi[i].move=0;
qi[i].leaffor=0;
}
qi[1].num=1;
qi[1].parent=-1;
qi[1].leaf=-1;
qi[1].deep=1;
for(i=2;i<=n;i++){
scanf("%d %d",&a,&b);
qi[b].num=b;
qi[b].parent=a;
qi[a].childnum++;
}
condeep(n);//cengji
for(i=1;i<=n;i++)
if(qi[i].childnum==0){
qi[i].leaf=1;
qi[i].leaffor=1;
scanf("%d",&qi[i].win);
}
setwin(n);
i=1;
while(1){
i=qi[i].move;
if(qi[i].move==0)
break;
}
printf("%d",qi[i].win);
return 0;
}
H 黑 暗 之 魂
题目描述
判断一年份
这一年的年份需要满足如下条件
是一个质数:即除了 1 和它本身以外不再有其他因数。
是闰年: 四年一闰,百年不闰,四百年再闰
给出特定的年份,请你判断一下这一年黑暗是否会降临
这个题目可以说是一个智力检测题了,想想看答案有几种可能。
I 对抗
题目描述
为了让大家对算法更深的理解,助教们决定以分组对抗的形式举办一场比赛。一共有n名同学。每个同学都有一个编号——从1到n的不同的整数。
比赛分多轮,每轮全员参加比赛。同学们被分成两个组,各组可能有不同数量的同学,但每个组必须至少有一名同学。位于不同组的同学之间存在对抗关系。
为了让大家在比赛中被充分调动,助教们希望每对同学都有过对抗关系。但是由于每轮比赛的安排耗费大量精力,助教们希望轮数越少越好。
请你帮助教们安排分组方案。
输入
输入仅一行。
仅一个数为学生人数n(2 ≤ n ≤ 1000)。
输出
第一行输出m——最少轮次
然后有m行输出
第i+1行中,先输出 fi ——第i轮第一组的人数(1 ≤ fi < n) ,以及fi个1到n之间的数字,对应第一组的同学编号
在这轮中,其他同学将在第二组。数字间用空格分隔。同学编号顺序任意
如果有多个最佳解决方案,请输出其中任何一个。
输入样例
5
输出样例
3
1 1
3 1 2 3
2 2 4
分析
这个题目有些难,是这个上机中我做的最久的一道题。本着不浪费的原则,只要两个人进行过对决就可以将他们合并起来,在后面的分组里,一起参与分组。
在写这个代码时,我还没有学习并查集的知识,现在想来并查集对于解决这个题可能更为方便。有兴趣的同学可以试试。
下面是我自己的代码,仅供参考。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<math.h>
int bat[1005][1000];
void battle(int n,int deep){
int i,j,sum=0,team;
i=1;j=1;
for(i=1;i<=(n/2);i++){
j=1;
while(bat[i][j]!=0){
if(bat[i][j]>0)
sum++;
j++;
}
}
if(n%2==0){
printf("%d ",sum);
for(i=1;i<=(n/2);i++){
j=1;
while(bat[i][j]!=0){
if(bat[i][j]>0)
printf("%d ",bat[i][j]);
j++;
}
}
printf("\n");
team=j-1;
for(i=1;i<=(n/2);i++){
j=1;
while(bat[i][j]!=0){
bat[i][j+team]=bat[i+n/2][j];
j++;
}
}
n=n/2;
if(n==1)
exit(0);
battle(n,++deep);
}
else{
j=1;
printf("%d ",sum);
for(i=1;i<=(n/2);i++){
j=1;
while(bat[i][j]!=0){
if(bat[i][j]>0)
printf("%d ",bat[i][j]);
j++;
}
}
printf("\n");
team=j-1;
for(i=1;i<=(n/2);i++){
j=1;
while(bat[i][j]!=0){
bat[i][j+team]=bat[i+n/2][j];
j++;
}
}
for(j=1;j<=team;j++)
bat[n/2+1][j]=-1;
j=1;
while(bat[n][j]!=0){
bat[n/2+1][j+team]=bat[n][j];
j++;
}
n=n/2+1;
battle(n,++deep);
}
}
int chance(int n){
int deep=0;
while(n!=1){
if(n%2==0){
deep++;
n=n/2;
}
else{
deep++;
n=n/2+1;
}
}
return deep;
}
int main()
{
int n, i , num = 0;
scanf("%d",&n);
for(i=1;i<=n;i++)
bat[i][1]=i;
num=chance(n);
printf("%d\n",num);
battle(n,1);
return 0;
}
I 对抗
题目描述
查找输入数据在data数组中的第一次出现的位置。
输入
第一行,一个正整数n(n <= 1000000);
第二行,n个int型整数x(可能有重复的数),每一个元素x记为数组data的数组元素;
第三行,多个int型整数(可能有重复的数,但总数不超过1000000个),每一个数记为t。输入的数之间用空白符号(空格、换行、或制表符)分割。
输出
对每一个t,若t出现在数组data中,则输出它第一次出现的位置(出现在data中第几个输入的数),否则输出NO。每一个输出占一行。
输入样例
5
1 2 3 4 5
4 5 6 7 1
输出样例
4
5
NO
NO
1
分析
这个题目有些难,但是可以看出题目的数据量很大不能暴力解决。二分对于这种查找可以说是很方便的。需要注意的是,可能有相同的数字,所以在查找时不能找到就停止,需要不断向左进行二分直到左侧再无目标数字。
下面是我自己的代码,仅供参考。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<math.h>
int shu[1000005][2];
int order[1000005];
int L[500005];
int R[500005];
int F[500005];
int Y[500005];
void megre(int shu[][2],int p,int q,int r) {
int n1, n2, i, j, k;
n1 = q - p + 1;
n2 = r - q;
for (i = 1; i <= n1; i++){
L[i] = shu[p + i - 1][0];
F[i]=shu[p+i-1][1];
}
for (j = 1; j <= n2; j++){
R[j] = shu[q + j][0];
Y[j]=shu[q+j][1];
}
L[i]=0x7fffffff;
R[j]=0x7fffffff;
i = 1, j = 1;
for (k = p; k <= r; k++) {
if (L[i] <= R[j]){
shu[k][0] = L[i];
shu[k][1]=F[i];
i++;
}
else{
shu[k][0] = R[j];
shu[k][1]=Y[j];
j++;
}
}
}
void megre_sort(int shu[][2], int p,int r) {
int q;
if (p < r){
q = (p + r) / 2;
megre_sort(shu, p, q);
megre_sort(shu, q + 1, r);
megre(shu, p, q, r);
}
}
void chack(int t,int n){
int low = 1;
int high = n ;
int mid,midnum;
while(low<= high){
mid = (low + high)/2;
midnum = shu[mid][0];
if(midnum<t)
low = mid + 1;
else if(midnum>t)
high = mid - 1;
else{
if(shu[mid-1][0]==t){
high=mid-1;
}
else{
printf("%d\n",shu[mid][1]);
return;
}
}
}
printf("NO\n");
}
int main(){
int n, i,t;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d",&shu[i][0]);
shu[i][1]=i;
}
megre_sort(shu,1,n);
while(~scanf("%d",&t)){
chack(t,n);
}
return 0;
}
总结
本篇博客是笔者的第一篇博客,不足之处还请大家指正。