北航2020级算法分析与设计-C1

说在最开始的话

这篇博客仅仅是提供学习资料和我个人的复习使用,在学习算法的过程中代码还是要自己亲手打一遍,不要图方便直接复制粘贴代码。复制代码百害而无一利。

前言

写这个系列博客的初衷是学习算法的过程有些粗糙,对于各个算法的掌握也是不太熟练,再加上复习时苦于没有往年题目的痛苦。为了自己和学弟学妹在算法上能有所进步,开坑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;
}

总结

本篇博客是笔者的第一篇博客,不足之处还请大家指正。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值