说在最开始的话
这篇博客仅仅是提供学习资料和我个人的复习使用,在学习算法的过程中代码还是要自己亲手打一遍,不要图方便直接复制粘贴代码。复制代码百害而无一利。
前言
写这个系列博客的初衷是学习算法的过程有些粗糙,对于各个算法的掌握也是不太熟练,再加上复习时苦于没有往年题目的痛苦。为了自己和学弟学妹在算法上能有所进步,开坑20级算法分析与设计。
本次更新C2 C2更多是对于数据结构和c语言基础的复习,涉及算法的部分不多,大家可以看一下自己的知识掌握如何。
A 这一刻我已等待许久
题目描述
众所周知,所有电子设备在霍格沃兹都会失去作用。因此 Alex 只要打开这封信,他就不用出题了。
他在信上了解到,他需要购买一件巫师袍,一只猫头鹰和一根魔杖。
Alex 走进奥利凡德魔杖店,被告知 “魔杖选择巫师”
人生而不同,魔杖会根据一个人的勇气,忠诚,智慧,野心的高低来选择合适的巫师
奥利凡德很轻松的就找到了 Alex 的四项属性值 a,b,c,d。
将魔杖标号,每一根魔杖对主人的要求也可以概括为这四项属性 Ai,Bi,Ci,Di
Alex 希望自己能够得到与自己契合度最高的魔杖,也就是使得 |a−Ai|+|b−Bi|+|c−Ci|+|d−Di| 最小。他应该买第几根魔杖呢?
对了,标号越大的魔杖价格也越高,因此契合度相同时, Alex 希望优先选购便宜的那个。
输入
第一行五个正整数 n,a,b,c,d
第二行 n 个正整数 A1,A2,⋯,An
第三行 n 个正整数 B1,B2,⋯,Bn
第四行 n 个正整数 C1,C2,⋯,Cn
第五行 n 个正整数 D1,D2,⋯,Dn
n≤1000, 属性值均在 [1,100] 范围内
输出
输出一行表示答案
分析
这个题目可以说很简单,直接放代码
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
int A[1005];
int B[1005];
int C[1005];
int D[1005];
int main()
{
int n,a,b,c,d,i,result,temp,min=0x7fffffff;
char ch;
scanf("%d %d %d %d %d",&n,&a,&b,&c,&d);
for(i=1;i<=n;i++)
scanf("%d",&A[i]);
for(i=1;i<=n;i++)
scanf("%d",&B[i]);
for(i=1;i<=n;i++)
scanf("%d",&C[i]);
for(i=1;i<=n;i++)
scanf("%d",&D[i]);
for(i=1;i<=n;i++){
temp=abs(a-A[i])+abs(b-B[i])+abs(c-C[i])+abs(d-D[i]);
if(temp<min){
min=temp;
result=i;
}
}
printf("%d",result);
return 0;
}
B 卡特兰数
题目描述
二叉树是一种重要的数据结构。
有1个节点的二叉树有1种,有2个节点二叉树有2种,有3个节点的二叉树有5种,有n个节点的二叉树记为h(n)种,h(n)又被称为卡特兰数。
现在,给出多组数据,每一组数据会给出一个正整数n。对于每一组数据,请输出h(n)的值。
输入
第一行一个正整数T,表示数据组数。
接下来T行,每行一个正整数n。
输出
对于每一行输入,输出h(n)的值。
分析
这个题目依旧难度不大,简单的递归即可
下面是我自己的代码
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
long long int h(int jie){
if(jie==1)
return 1;
else
return ((4*jie-2)*h(jie-1)/(jie+1));
}
int main()
{
int n,i,jie;
long long int result;
scanf("%d",&n);
while(n--){
scanf("%d",&jie);
result=h(jie);
printf("%lld\n",result);
}
return 0;
}
C 公交排序
题目描述
Marvolo 在国庆的时候出去玩了一天,但不幸的是他选择的交通工具是公交车:因为假期出游的原因,车上人满为患,连移动到车门口下车都十分困难。
Marvolo 想:大家在哪一站下车一开始是无序的,可不可以先在公交车上调整成有序的(也就是单调不降的),这样下车的时候就不用着急往前挤了?如果要调整的话,最少要调整几次呢?因为车上人很多,规定一次调整只能交换相邻两个人的顺序。
为了简化问题,你可以认为公交车上一共有n个人,第i个人要在第ai个站下车。最开头那个人站的位置是车门位置。
举个栗子,假如有3个人,这三个人分别在第3,1,2站下车。先让前两个人交换顺序,变成1,3,2,再让后两个人交换顺序,变成1,2,3,就可以调整成有序的,此时调整次数最少。。
输入
第一行一个正整数n,表示人数。
接下来一行,有n个正整数,第i个数表示ai。
输出
输出一个整数,表示最少交换几次。
输入样例
5
1 3 4 5 2
输出样例
3
分析
这个题目有些难度,需要用到mergesort二分算法。
但是仔细想一想换位置的过程,其实和二分的过程很像,看具体代码可能更容易理解,
下面是我自己的代码。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
int a[200005];
int L[500005];
int R[500005];
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;
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++;
}
}
}
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,jie;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
megre_sort(a,1,n);
printf("%lld",count);
return 0;
}
D 搬题
题目描述
Marvolo不会出题了,于是他决定去搬题。但是他有个奇怪的癖好,在第k天时,他必须要搬来k道题,否则他当场罢工。
Marvolo现在找到了一个题库,一共有n场比赛,每一场比赛中有一定数量的题目。但是他十分挑剔,每场比赛他只会搬一天,每一天也只能搬一场比赛里的题目,之后那场比赛就会被弃之不用。对于每场比赛,他不必要搬来所有的题。
那么问题来了,Marvolo最多会工作几天才罢工呢?
输入
第一行,1个数为n(1≤n≤106) ,表示有多少场比赛。
第二行,n个整数a1,a2,...,an(1≤ai≤109),分别表示每场比赛有多少道题。
输出
输出一行,Marvolo罢工前最多工作天数。
输入样例
4
3 1 4 1
输出样例
3
分析
这个题目难度不大,稍微复杂的点就是相同题目数的比赛需要判断一下。
下面是我自己的代码,仅供参考。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
int a[1000005];
int use[1000005];
int cmp(const void * a,const void *b){
return *(int *)a - *(int *)b;
}
int search(int start,int k,int n){
int i;
for(i=start+1;i<=n;i++)
if(a[i]>=k&&use[i]==0){
use[i]=1;
return i;
}
return 0;
}
int main()
{
int n,i,type=0,k;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
qsort(a+1,n,sizeof(int),cmp);
k=0;
while(++k){
type=search(type,k,n);
if(type==0)
break;
}
printf("%d",k-1);
return 0;
}
E 最大子数组
题目描述
定义一个数组的和最大的连续非空子数组为该数组的最大子数组。
给你一个数组,求其最大子数组的和。
输入
第一行一个数为数组长度n(1≤n≤106)
第二行包含 n 个整数 a1,a2,...,an(−109≤ai≤109),表示数组。
输出
输出一行一个数,最大子数组的和。
输入样例
4
1 2 3 4
输出样例
10
分析
这个题目就是经典的最大子数组的模板题,算法导论p40有详细的解释,大家可以学习一下。
下面是我自己的代码,仅供参考。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
typedef struct node Node;
struct node{
int left;
int right;
long long int sum;
};
int a[1000005];
long long int max;
Node findcross(int a[],int low,int mid,int high){
long long int leftsum=-9223372036854775808,rightsum=-9223372036854775808,sum;
int i,j,maxleft,maxright;
Node res;
sum=0;
for(i=mid;i>=low;i--){
sum=sum+a[i];
if(sum>leftsum){
leftsum=sum;
maxleft=i;
}
}
sum=0;
for(j=mid+1;j<=high;j++){
sum=sum+a[j];
if(sum>rightsum){
rightsum=sum;
maxright=j;
}
}
res.left=maxleft;
res.right=maxright;
res.sum=leftsum+rightsum;
return res;
}
Node findmaxsub(int a[],int low,int high){
int mid;
Node A,B,C,res;
long long int rightsum,leftsum,crosssum;
if(low==high){
res.left=low;res.right=high;res.sum=a[low];
return res;
}
else{
mid=(low+high)/2;
A=findmaxsub(a,low,mid);
B=findmaxsub(a,mid+1,high);
C=findcross(a,low,mid,high) ;
if(A.sum>=B.sum&&A.sum>=C.sum)
return A;
else if(B.sum>=A.sum&&B.sum>=C.sum)
return B;
else
return C;
}
}
int main()
{
int n,i,type=0,k;
Node res;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
res=findmaxsub(a,1,n);
printf("%lld",res.sum);
return 0;
}
F 二分查找++
题目描述
给一个已经排好序的长度为n的数组a,第i个元素是ai。
接下来有m条询问,每个询问的格式为op x。如果op=1,表示问x第一次出现的位置是什么;如果op=2,表示询问x最后一次出现的位置是什么。如果x从未出现过,输出Not Found。
输入
第一行有两个正整数n,m。
接下来一行,一共n个正整数,由空格隔开,表示数组a。保证是已经从小到大排好序的。
接下来m行,每行两个正整数op x,含义如上所示。
输出
对于每一个询问操作,输出查询结果,每个一行。
输入样例
5 4
1 2 2 3 4
1 2
2 2
1 3
1 5
输出样例
2
3
4
Not Found
分析
这个题目和c1中的题有相似之处,大家可以转去看看。
下面是我自己的代码,仅供参考。
#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("Not Found\n");
}
void chack2(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[high][0]==t){
printf("%d\n",shu[high][1]);
return;
}
else {
high--;
}
}
}
printf("Not Found\n");
}
int main(){
int n,m,i,t,type,tag;
scanf("%d %d",&n,&m);
for(i=1;i<=n;i++){
scanf("%d",&shu[i][0]);
shu[i][1]=i;
}
megre_sort(shu,1,n);
while(m--){
scanf("%d %d",&type,&tag);
if(type==1)
chack(tag,n);
else
chack2(tag,n);
}
return 0;
}
G Alex 和 Marvolo 不会出题了 - 3
题目描述
Alex 和 Marvolo 不会出题了。于是他们开始闲聊:
Alex: 感觉他们现在的警觉性很高。
Marvolo: 同感,上一道题的 hint 已经成为表情包了。
Alex: 我决定金盆洗手,改邪归正。出一点有难度有挑战性的算法题
Marvolo: 哦吼?
Alex: 你平时看电视嘛?
Marvolo: 不怎么看,我都玩手机的。
Alex: 对,电视上面的节目都是限时的,你过了这个点就看不到了。
Marvolo: 手机上看不就行了。
Alex: no no no,一般来说电视上是最早播出来的,电视放完了手机上才更新。
Marvolo: 所以呢?
Alex: (翻了一会儿书包,掏出一张密密麻麻的纸)这里有所有电视台的节目单,包括每个节目的起始时间和结束时间。你说如果我时间充裕,我一天能看完多少节目?
Marvolo: 唔,晚上 23:00 我追的番才更新,所以我一天只看一个。
简而言之,一共有 n(1≤n≤106) 个节目,分别在不同的电台上播放。给定每个节目的起始时间和结束时间 si,ei,(1≤si,ei≤105),假设时间无限,最多可以看多少个不同的节目呢?
输入
第一行一个正整数 n(1≤n≤106) 接下来的 n 行,每行 2 个正整数 si,ei(1≤si<ei≤105) ,表示一个节目的起止时间。
输出
输出一行一个正整数,表示最多可以看的节目数量
输入样例
3
1 3
3 4
1 4
输出样例
2
分析
一道贪心问题,节目结束的越早必定对于观看计划来说越有利,因此将节目排序后,可算出有多少节目可以按时观看。
下面是我自己的代码,仅供参考。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<math.h>
typedef struct node *nodeptr;
typedef struct node Node;
struct node{
int s;
int e;
};
Node a[1000005];
int cmp(const void *a, const void *b){
Node x = *(Node*)a, y = *(Node*)b;
if(x.e != y.e) return x.e - y.e;
return x.s - y.s;
}
int main(){
int n,count=0,i;
Node last;
last.e=0;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d %d",&a[i].s,&a[i].e);
}
qsort(a+1,n,sizeof(Node),cmp);
for(i=1;i<=n;i++){
if(a[i].s>=last.e){
last=a[i];
count++;
}
}
printf("%d",count);
return 0;
}
H 黑 暗 之 魂
题目描述
打开了神秘的盒子之后,你看到里面放着一株绿藤,绿藤即将枯萎,需要神奇的雨水浇灌。
输入 n 个非负整数,每个整数表示一个宽度为 1 的方形柱子的高度,计算按此排列的 n 个柱子的高度图,下雨之后能接多少雨水?
样例:假设输入的 n 个数为 [0,1,0,2,1,0,1,3,2,1,2,1],代表如下图所示的柱子高度图,该图能接的总雨水量为 6。
输入
第一行一个整数 n ( 1≤n≤106 ),含义如上所示。
第二行 n 个非负整数 a1,a2,...,an( 0≤ai≤106 ),代表柱子的高度。
输出
输出一行一个整数,表示接到的总雨水量。
输入样例
12
0 1 0 2 1 0 1 3 2 1 2 1
输出样例
6
分析
这个题目有些难,我是在上机过程中并未做出,但讲题课有同学提供的用栈存储目前呈凹型的部分直到遇见下一个凹形后,一一退栈计算面积即可。
I 最近点对
题目描述
某行星上有一种生物,这种生物有一个特点,它们只能够沿着平行于x轴和平行于y轴两个方向行走,现在已知该行星上该物种的种群数量为n,且知道它们每一个个体的位置,求它们之间的最小距离。
如下图,
假设x轴为水平方向,y轴为竖直方向。 红色、灰色和土黄色都是生物A能够行走到B的路线,当然,这只是其中的一部分,A有无数种方法走到B,只要沿着平行于x轴或是平行于y轴即可。
输入
第一个数为种群数量n
接下来n行,每行两个浮点数x、y,表示坐标
输出
一个浮点数,表示最小的距离。保留3位小数
输入样例
2
0.00 0.00
1.00 2.00
输出样例
3.000
分析
这个题目有些难,是一个经典的模板题,在这里不过多赘述,请大家看p613 33.4
下面是我自己的代码,仅供参考。
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int n;
struct node
{
double x, y;
}ar[100050];
int br[100050];
//用于求两点间距离
double dis(struct node a, struct node b)
{
return fabs(a.x-b.x)+fabs(a.y-b.y);
}
bool cmp1(struct node a, struct node b)
{
return a.x < b.x;
}
bool cmp2(int a, int b)
{
return ar[a].y < ar[b].y;
}
double near_dis(int l, int r)
{
//两个递归终点,只剩两个点或三个点时,两两求距离然后取min
if(r == l + 1)
return dis(ar[l], ar[r]);
if(l + 2 == r)
return min(dis(ar[l], ar[r]), min(dis(ar[l], ar[l + 1]), dis(ar[l + 1], ar[r])));
//分割,求出情况1, 2的结果
int mid = (l + r)/2;
double ans = min(near_dis(l, mid), near_dis(mid + 1, r));
//找出符合条件的点,并将其按y坐标排序
int cnt = 0;
for(int i = l; i <= r; ++i)
{
if(ar[i].x >= ar[mid].x - ans && ar[i].x <= ar[mid].x + ans)
br[++cnt] = i;
}
sort(br + 1, br + cnt + 1, cmp2);
//求出情况2的答案,不断取min更新
for(int i = 1; i <= cnt; ++i)
{
for(int j = i + 1; j <= cnt; ++j)
{
if(ar[br[j]].y - ar[br[i]].y >= ans) break;
ans = min(ans, dis(ar[br[i]], ar[br[j]]));
}
}
return ans;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%lf %lf", &ar[i].x, &ar[i].y);
sort(ar + 1, ar + n + 1, cmp1);//按x排序
printf("%.3f",near_dis(1,n));
return 0;
}
总结
不足之处还请大家指正。