一篇不定时更新的刷题记吖

目录

201609-2 火车购票

思路

201709-2 公共钥匙盒

思路

解法一

解法二

201503-2 数字排序

图的遍历及连通分量

解法一:

解法二:

选数

二叉树的深度

Acwing823 排列

思路

无限01字符串

思路

数的美丽值

神奇的集合

爱下单的母鸡

安排座位

思路

前k个数

问题分析:

源代码:

问题反思:


201609-2 火车购票

计算机软件能力认证考试系统

问题描述

  请实现一个铁路购票系统的简单座位分配算法,来处理一节车厢的座位分配。
  假设一节车厢有20排、每一排5个座位。为方便起见,我们用1到100来给所有的座位编号,第一排是1到5号,第二排是6到10号,依次类推,第20排是96到100号。
  购票时,一个人可能购一张或多张票,最多不超过5张。如果这几张票可以安排在同一排编号相邻的座位,则应该安排在编号最小的相邻座位。否则应该安排在编号最小的几个空座位中(不考虑是否相邻)。
  假设初始时车票全部未被购买,现在给了一些购票指令,请你处理这些指令。

输入格式

  输入的第一行包含一个整数n,表示购票指令的数量。
  第二行包含n个整数,每个整数p在1到5之间,表示要购入的票数,相邻的两个数之间使用一个空格分隔。

输出格式

  输出n行,每行对应一条指令的处理结果。
  对于购票指令p,输出p张车票的编号,按从小到大排序。

样例输入

4
2 5 4 2

样例输出

1 2
6 7 8 9 10
11 12 13 14
3 4

样例说明

  1) 购2张票,得到座位1、2。
  2) 购5张票,得到座位6至10。
  3) 购4张票,得到座位11至14。
  4) 购2张票,得到座位3、4。

评测用例规模与约定

  对于所有评测用例,1 ≤ n ≤ 100,所有购票数量之和不超过100。

思路

贪心问题,每次都找出最好的情况。

需要注意一点就是,因为每次都是买相邻的座位,所以不存在座位出现参差的情况。(在二刷的时候花了一些时间想这个问题)

#include<iostream>
#include<queue> 
using namespace std;

const int N=21;
queue<int> q[N];  //使用队列,是为了方便直接得到每一排的剩余座位的数目


int main(){
	int j=1;
    //初始化,将1~100存入队列
	for(int i=1;i<=20;i++){
		int cnt=5;
		while(cnt--){
			q[i].push(j++);
		}
	}
    //输入n
	int n;
	cin>>n;
    //n组数据
	for(int i=0;i<n;i++){
		int num;
		cin>>num;
        //先遍历20排座位,查看是否有某一排座位能提供连续的座位
		for(int j=1;j<=20;j++){
			if(q[j].size()>=num){  //找到一排
				while(num>0){      //依次输出座位
					cout<<q[j].front()<<" ";
					q[j].pop();
					num--;
				}
				cout<<endl;
				break;
			}
		}
        //如果所需座位量不等于0,说明不能直接在同一排购买连续的票    
		if(num!=0){  
            //遍历20排,依次输出座位直到num=0
			for(int j=1;j<=20&&num>0;j++){
				while(!q[j].empty()&&num>0){
					cout<<q[j].front()<<" ";
					q[j].pop();	
					num--;
				}
			}
			cout<<endl;
		}
	}
	return 0;
} 

201709-2 公共钥匙盒

计算机软件能力认证考试系统

问题描述

  有一个学校的老师共用N个教室,按照规定,所有的钥匙都必须放在公共钥匙盒里,老师不能带钥匙回家。每次老师上课前,都从公共钥匙盒里找到自己上课的教室的钥匙去开门,上完课后,再将钥匙放回到钥匙盒中。
  钥匙盒一共有N个挂钩,从左到右排成一排,用来挂N个教室的钥匙。一串钥匙没有固定的悬挂位置,但钥匙上有标识,所以老师们不会弄混钥匙。
  每次取钥匙的时候,老师们都会找到自己所需要的钥匙将其取走,而不会移动其他钥匙。每次还钥匙的时候,还钥匙的老师会找到最左边的空的挂钩,将钥匙挂在这个挂钩上。如果有多位老师还钥匙,则他们按钥匙编号从小到大的顺序还。如果同一时刻既有老师还钥匙又有老师取钥匙,则老师们会先将钥匙全还回去再取出。
  今天开始的时候钥匙是按编号从小到大的顺序放在钥匙盒里的。有K位老师要上课,给出每位老师所需要的钥匙、开始上课的时间和上课的时长,假设下课时间就是还钥匙时间,请问最终钥匙盒里面钥匙的顺序是怎样的?

输入格式

  输入的第一行包含两个整数NK
  接下来K行,每行三个整数wsc,分别表示一位老师要使用的钥匙编号、开始上课的时间和上课的时长。可能有多位老师使用同一把钥匙,但是老师使用钥匙的时间不会重叠。
  保证输入数据满足输入格式,你不用检查数据合法性。

输出格式

  输出一行,包含N个整数,相邻整数间用一个空格分隔,依次表示每个挂钩上挂的钥匙编号。

样例输入

5 2
4 3 3
2 2 7

样例输出

1 4 3 2 5

样例说明

  第一位老师从时刻3开始使用4号教室的钥匙,使用3单位时间,所以在时刻6还钥匙。第二位老师从时刻2开始使用钥匙,使用7单位时间,所以在时刻9还钥匙。
  每个关键时刻后的钥匙状态如下(X表示空):
  时刻2后为1X345;
  时刻3后为1X3X5;
  时刻6后为143X5;
  时刻9后为14325。

样例输入

5 7
1 1 14
3 3 12
1 15 12
2 7 20
3 18 12
4 21 19
5 30 9

样例输出

1 2 3 5 4

评测用例规模与约定

  对于30%的评测用例,1 ≤ NK ≤ 10, 1 ≤ w ≤ N, 1 ≤ sc ≤ 30;
  对于60%的评测用例,1 ≤ NK ≤ 50,1 ≤ w ≤ N,1 ≤ s ≤ 300,1 ≤ c ≤ 50;
  对于所有评测用例,1 ≤ NK ≤ 1000,1 ≤ w ≤ N,1 ≤ s ≤ 10000,1 ≤ c ≤ 100。

思路

解法一

可以将需要进行的操作一次排序。再用数组模拟,遍历每一次操作。

每一个老师都有一次拿钥匙和取钥匙的操作,同时也对应着两个时间节点。我们把每一个时间节点进行的每一个操作记录下来,在排序的时候根据题干要求用自定义的cmp进行排序。

#include<iostream>
#include<algorithm>
using namespace std;

const int K=2010;
const int N=1010;  
int n,k;

struct Data{
	int id;
	int time;
	int is;
};

struct Data data[K];

bool cmp(struct Data a,struct Data b){
	if(a.time!=b.time) return a.time<b.time;
	else if(a.is!=b.is) return a.is>b.is;   //先还后借
	else if(a.is==b.is) return a.id<b.id;   //如果同一时间还或者借,编号越小越先还越先借
} 

int main(){
	cin>>n>>k;
	int x=0;
	for(int i=0;i<k;i++){
		int w,s,c;
		cin>>w>>s>>c;
		data[x].id=w;
		data[x].time=s;
		data[x].is=0;    //借 
		x++;
		data[x].id=w;
		data[x].time=s+c;
		data[x].is=1;	 //还 
		x++; 
	}
	sort(data,data+x,cmp);
	
	int array[N];
	for(int i=1;i<=n;i++) array[i]=i; 
	for(int i=0;i<x;i++){
		if(data[i].is==0){  //借 
			int j;
			for(j=1;j<=n;j++){  //找到钥匙 
				if(array[j]==data[i].id) break;
			} 
			array[j]=0;  //设为空 
		}
		else{              //还 
			int j;
			for(j=1;j<=n;j++){  //从左到右找到第一个空 
				if(array[j]==0) break;
			}
			array[j]=data[i].id;  //把钥匙还回去 
		} 
	}
	for(int i=1;i<=n;i++) cout<<array[i]<<" ";
	return 0;	
}

解法二

201503-2 数字排序

计算机软件能力认证考试系统

问题描述

  给定n个整数,请统计出每个整数出现的次数,按出现次数从多到少的顺序输出。

输入格式

  输入的第一行包含一个整数n,表示给定数字的个数。
  第二行包含n个整数,相邻的整数之间用一个空格分隔,表示所给定的整数。

输出格式

  输出多行,每行包含两个整数,分别表示一个给定的整数和它出现的次数。按出现次数递减的顺序输出。如果两个整数出现的次数一样多,则先输出值较小的,然后输出值较大的。

样例输入

12
5 2 3 3 1 3 4 2 5 2 3 5

样例输出

3 4
2 3
5 3
1 1
4 1

评测用例规模与约定

  1 ≤ n ≤ 1000,给出的数都是不超过1000的非负整数。

思路

第一眼,统计次数,直觉桶排序。可是一看,还需要对次数进行排序。那我们就用结构体自制一个桶进行排序吧。

#include<iostream>
#include<algorithm> 
using namespace std;

const int N=1010;
int n,x;
//可以离散化,但我不会 

struct Data{
	int id;
	int num;
};
struct Data data[N];
bool cmp(struct Data a,struct Data b){
	if(a.num!=b.num) return a.num>b.num;
	else return a.id<b.id;
}

int main(){
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>x;
		data[x].id=x;
		data[x].num++;
	}
	sort(data,data+N,cmp);
	int i=0;
	while(data[i].num!=0){
		cout<<data[i].id<<" "<<data[i].num<<endl;
		i++;
	}
	return 0;
}

图的遍历及连通分量

题目描述:

根据输入的图的邻接矩阵A,求此图的连通分量的个数,升序输出每个连通分量所包含的顶点。(顶点从0开始编号)

【样例说明】

 邻接矩阵中对角线上的元素都用0表示。(单个独立结点,即与其它结点都没有边连接,也算一个连通分量)

输入

输入描述

第一行为图的结点个数n,之后的n行为邻接矩阵的内容,每行n个数表示。其中A[i][j]=1表示两个结点邻接,而A[i][j]=0表示两个结点无邻接关系。

输出

输出描述描述:

第一行为此图连通分量的个数k。

接下来k行,每行为各连通分量所包含的顶点升序输出)

解法一:

思路:

深搜记录连通分量的个数,在进行一次深搜,边搜边输出。

#include<iostream>
using namespace std;
//图的遍历
const int N=110;
int n;
int vex[N][N],visited1[N],visited2[N];
int liantong[N][N],x;

void DFS1(int v){
	if(visited2[v]==0){
		cout<<v<<" ";
		visited2[v]=1;
		for(int i=0;i<n;i++){
			if(vex[v][i]==1){
				DFS1(i);
			} 
		}
	}
}

void DFS2(int v){
	if(visited1[v]==0){
		visited1[v]=1;
		for(int i=0;i<n;i++){
			if(vex[v][i]==1){
				DFS2(i);
			} 
		}
	}
}

int main(){
	cin>>n;
	int ans;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++) cin>>vex[i][j];
	}
	for(int i=0;i<n;i++){
		if(visited1[i]==0){
			ans++;
			DFS2(i);
		}
	}
	cout<<ans<<endl;
	for(int i=0;i<n;i++){
		if(visited2[i]==0){
			DFS1(i);
			cout<<endl;
		}
	}
	return 0;
}

解法二:

思路:

并查集,待更新

#include<iostream>
#include<algorithm>
using namespace std;
const int M=1e3+6;
int n,fa[M],tp,ans;
int find(int x){
	return (fa[x]==x)?x:fa[x]=find(fa[x]);
}
void join(int x,int y){
	int fx=find(x),fy=find(y);
	if(fx!=fy) fa[fx]=fy;
}
struct nt{
	int id,anc;
}a[M];
int cmp(nt x,nt y){
	if(x.anc!=y.anc)return x.anc<y.anc;
	return x.id<y.id;
}
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=0;i<n;i++)fa[i]=i;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			cin>>tp;
			if(tp){
				join(i,j);
			}
		}
	}
	for(int i=0;i<n;i++){
		a[i].id=i;
		a[i].anc=find(i);
		if(a[i].anc==i)ans++;
	}
	sort(a,a+n,cmp);
	cout<<ans<<'\n';
	int pre=a[0].anc;
	for(int i=0;i<n;i++){
		if(a[i].anc==pre)cout<<a[i].id<<' ';
		else cout<<'\n'<<a[i].id<<' ',pre=a[i].anc;
	}
	return 0;
}

/*
6
0 0 0 1 1 0
0 0 0 0 0 0
0 0 0 0 0 1
1 0 0 0 0 0
1 0 0 0 0 0
0 0 1 0 0 0
*/

选数

B-递归实现组合型枚举_0x02 基本算法-枚举、模拟、递推 (nowcoder.com)

题目描述

题目描述:在1到n这n个数中随机选出m个不同的数,输出所有可能的选择方案,要求从小到大输出。
 

输入

输入描述:一行输入两个整数n,m(1<=m<=n<=10)

输出
输出描述:按照从小到大的顺序输出所有方案,一行一个方案。

样例输入

3 2

样例输出

1 2
1 3
2 3

提示

此题来源于《算法设计与问题求解》第2章第1题

可以思考一下如果题干设置成同一数字可以多次使用,我们只需要简单修改一下即可

二叉树的深度

题目描述

题目描述:

给出一棵根节点为1的二叉树,和每个点的左右儿子节点,求这棵二叉树的深度。

输入

输入描述:

第一行输入二叉树的节点数n。 (1<=n<=1000)

接下来n行,每行输入两个整数l,r分别代表左儿子节点编号和右儿子节点编号,0代表节点为空。

输出

输出描述:

输出二叉树的深度

样例输入

5
2 3
0 0
4 5
0 0
0 0

样例输出

3

Acwing823 排列

823. 排列 - AcWing题库

题目描述

给定一个整数 nn,将数字 1∼n1∼n 排成一排,将会有很多种排列方法。

现在,请你按照字典序将所有的排列方法输出。

输入格式

共一行,包含一个整数 nn。

输出格式

按字典序输出所有排列方案,每个方案占一行。

数据范围

1≤n≤91≤n≤9

输入样例:

3

输出样例:

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

思路

无限01字符串

题目描述:

01字符串是一种只有字符0和1的字符串。有如下两种操作:

1. 翻转字符串(reverse),例如001变为100

2. 反转字符串(switch):0变为1,1变为0

有一个01字符串的无限序列:

S[1]=0

S[2]=001

S[3]=0010011

......

S[n]=S[n-1]+0+switch(reverse(S[n-1]))

现在给出一个整数k,求S[50]中第k个字符是什么。
 

输入

输入描述:

一行输入一个整数k,(1<=k<=1000000000000000)

输出

输出描述:

输出一个字符0或1

样例输入

2

样例输出

0

思路

简单汪两句细节:

1.先算出s[n]分别是多少,

2.判断k的位置

3.如果在左边,就把范围缩小

4.在右边就进行一次逆运算

5.在中间或者递归到了第一个位置就返回0

#include<iostream>
using namespace std;
typedef long long ll;

ll k,s[55];

int dfs(int n,ll k){
	if(n==1||k==s[n]/2+1) return 0;
	if(k<=s[n-1]) return dfs(n-1,k);
	else return 1^dfs(n-1,s[n]-k+1);
}


int main(){
	s[1]=1;
	cin>>k;
	for(int i=2;i<=50;i++) s[i]=2*s[i-1]+1;
	cout<<dfs(50,k);
	return 0;
} 

数的美丽值

题目描述

题目描述:

现有如下操作:对一个数x,将其所有位上的数相加,得到一个新的数。

给出一个数字n,不断对其进行上述操作,直到得到的数为个位数,并将其称之为数字n的美丽值,求最后得到的个位数。

输入

输入描述:

一行输入一个数字n  (1<=n<=1000000000)

输出

输出描述:

输出得到的个位数

样例输入

12

样例输出

3

神奇的集合

题目描述

题目描述:

有一个元素集合,它有如下性质:

1. k是集合中的一个元素。

2. 若y是集合中的一个元素,则y*2+1和y*3+1也是集合中的元素。

保证除了上述情况,无其他元素在集合内。

现给出两个整数k和x,求x是否在集合内。
 

输入

输入描述:

一行输入两个整数k,x。  (1<=k<=x<=100000)

输出

输出描述:

如果x是集合内的元素,则输出”YES”,反之,输出”NO”

样例输入

3 22

样例输出

YES

爱下单的母鸡

题目描述

题目描述:

有一只母鸡,每年能生下一只小母鸡,小母鸡在过了三年之后每年也能生下一只小母鸡,问第n年的时候,有多少只鸡。例如:在第一年,只有一只母鸡,在第二年,母鸡生下小鸡,有两只母鸡,这只小母鸡在第4年的时候会开始生下小母鸡。

输入

输入描述:

一行输入一个整数n。  (1<=n<=40)

输出

输出描述:

输出第n年有多少只母鸡。

样例输入

4

样例输出

5

安排座位

题目描述:

有一个豪华剧场,其台下座位可以看成是一个n*m的网格,为了提升观众的观看体验感,不被打扰,工作人员规定:如果某个位置有人坐,那么其上下左右四个位置都必须要空着,不能坐。已知有k个观众,求有多少种坐法是满足要求的。不考虑观众的不同性。

输入描述:

一行输入三个整数n,m,k。  (1<=n,m<=10 , 1<=k<=4)

输出描述:

输出一个整数,表示有多少种坐法。

样例输入

2 3 2

样例输出

8

思路

前k个数

问题分析:

直接用sort()函数可以,为了考察我们对快排的理解,我们可以直接快排。当然,在此基础上,可以考虑,题目仅仅是需要我们求出前k个数,当一个数组很长,但我们只需要前1个数,为了这一个数,把整个数组都排序一边是不是有点亏。开始考虑有没有一种方法,我们只用排前K个数呢?在仔细想一想,快排 有一种状态,分界点左边的数全部小于等于(大于等于)右边的数。只要其中一边的数的数目超过k,我们岂不是可以直接缩小排序区间?但由于我们排序的过程是按照从大到小(从小到大)的顺序排的,只需要大的那一边,那如果分解点划分出来大的那一边数目小于k怎么办捏?对了,可以对另外一边进行快排,把我们还需要的数放在前面来。

算法实现:

源代码:

#include<iostream>
#include<algorithm>
using namespace std;

const int N=1e5+10;
int q[N],n;
bool cmp(int a,int b){
       return a>b;   
}

void quick_sort(int l,int r,int k){
       if(l>=r) return;
       int i=l-1,j=r+1,x=q[(l+r)/2];
       //找到那个分界点,使得左边的数大于等于右边的数
       while(i<j){
              do i++;while(q[i]>x);
              do j--;while(q[j]<x);
              if(i<j) swap(q[i],q[j]);
       }
       int s=j-l+1;  //s表示该区间经过划分后左边的数的数目
       if(s==k) return;  //如果相等,就返回
       if(s>k) quick_sort(l,j,k);  //如果大于k,就排序左边,直到相等
       else quick_sort(j+1,r,k-s);    //如果小于,就排序右边,这时所需要的k值就变成了k-s
       return;
}

int main(){
       cin>>n;
       int k;
       for(int i=0;i<n;i++) cin>>q[i];
       cin>>k;
       quick_sort(0,n-1,k);
       sort(q,q+k,cmp);  //前面的快排只是将前k大的数集中到了前k个位置,此时他们时混乱的,所以需要再进行一次排序
       for(int i=0;i<k;i++) cout<<q[i]<<endl;
       return 0;

}

问题反思:

其实在思考的过程中我一直妄想再快排中也完成那一步sort()排序,陷入了死循环。因为当s==k时,如果对该区间在进行quick_sort()递归,又会出现s<k的情况了。当然如果题干变一下,求第k个数,或许可以一试?786. 第k个数 - AcWing题库

 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值