华南农 2024级 数据结构 小测题1

1  约瑟夫环

Time Limit:1000MS  Memory Limit:65535K

题型: 编程题   语言: G++;GCC

描述

约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,报数为第M个的将出列,下一个人接着重新开始报数,重复这个过程,最后剩下一人。例如N=6,M=5,出列的顺序是:5、4、6、2、3,最后1剩下。

现在有这么一个游戏来模拟约瑟夫问题,所有人围成一圈,且将人员分成两组,每组有k个人,前k个是第一组的人员,后k个是第二组的人员,假设你现在是第一组的组长,你必须确定最小m,使到所有第二组的人员都在第一组的人员前出列,
这样你们组就可以获胜。

输入格式

输入包含多行,每行输入一个k(0 < k < 14),表示每组的人员数目;
当输入k为0时表示输入结束。

输出格式

对于每个输入的非0的k输出单独一行,包含与输入k相对应的m。

输入样例

3
4
0

输出样例

5
30

杨老师给的题解思路:

解题思路: 对于这个约瑟夫问题,要求的就是把后k个一个个出局,而前k个都没有一个出局的,每一次要数到第 m 个人才出局,因为 m 可能很大,用链表逐个数时间复杂度不满足要求。因此采用顺序表实现,由于前k个都没有变,而只要数到的是后面k个任意一个就满足条件,所以每次只要是后k个的任意一个,只要出局最后一个人就行,即总人数n减1,在顺序表中从第i个位置报数到第 m 个采用模运算i= (i+m-1)%n,n是当前的总人数。依次从小大去枚举可能的 m 就行,如果在某一次枚举过程中出局了前k个人,则这个m不满足条件,可跳去枚举下一个 m。在 m 的枚举时,要注意 m 的初始值为k+1(保证第一次出去的肯定不是前k个),另外如果 m 是满足条件的,则当最后只剩 k+1 人时,一定是K个第一组的人和1个第二组的人,此时只能是两种情况:1..k(上一步出局)x或者 1..kx(上一步出局),由此可得m需要满足是(k+1)的倍数或者是(k+1)的倍数+1的值。

但是我还是不太会实现TAT    打了还是超时,遂打表

这里是打表

#include <iostream>
#include <vector>
using namespace std;
int a[]={0,2,7,5,30,169,441,1872,7632,1740,93313,459901,1358657,2504881};
int main() {
	int k;cin>>k;
	while(k){
		cout<<a[k]<<endl;
		cin>>k;
	}
    return 0;
}    

以下是超时代码实现(但是可以用来打表,其实还是挺快的QwQ)。

能不能来个大佬教教怎么不超时orz

#include <iostream>
#include <vector>
using namespace std;
int k;
int ysf( int m) {
	int n=2*k;
    vector<int> p(n);//可以看作是int p[n]
    for (int i=0;i<n;i++) p[i]=i+1;
//初始化,p[n]表示顺序表中第n个人对应的编号
    int j=0;
//去掉k个人即可结束
    for(int i=0;i<k;i++){
    	j=(j+m-1)%n;
//更新下标,j表示每一次数到的要被出局的人
    	if(p[j]<=k) return 0;
//如果该出局的人的编号小于等于k就玩完了
    	p.erase(p.begin()+j);
    	n--;
	}
    return 1;
}
int main() {
	cin>>k;
	while(k){
		int n=2*k;
//从k+1开始枚举,就不会一开始就排掉k以及前面的人了。
		for(int m=k+1;;m++){
			if(ysf(m)){
				cout<<m<<endl;
				break;
			}
		}
		cin>>k;
	}
    return 0;
}    

2  停车管理

Time Limit:1000MS  Memory Limit:65535K

题型: 编程题   语言: G++;GCC;VC;JAVA;PYTHON

描述

设某银行停车场内只有一个可停放 n 辆汽车的狭长通道(宽度仅容许一辆车驶入,尽头是死胡同),且只有一个大门可供汽车进出。汽车在停车场内按车辆到达时间的先后顺序依次排列。
若车场内已停满 n 辆汽车,则后来的汽车只能在门外便道上等候,一旦有车开走,则排在便道上的第一辆车即可开入停车场;如果有车要开进来的同时有车要开出,则优先让车开出来;
车主停好车后会离开停车场去银行办理业务,业务办理结束后回到停车场内要离开时,只能等待在它之后开入的车辆都离开停车场后才能开出。
现在请你编写一个程序计算每个车主的总停留时间(从到达到离开停车场的总时间)。

输入格式:
第一行输入两个个整数n,m;n表示停车场能容纳的汽车总数,m表示需要进入停车场的车辆数目。
接着输入m行,每行由两个整数组成,分别表示第i辆车的到达时间和其车主在银行办理业务所花的时长,已按车辆到达的时间排序。
输出格式:
输出m行,每行分别表示第i辆车的总停留时间。

输入样例:
3 5
1 3
2 5
3 6
4 1
5 3

输出样例:
8
7
6
8
7

提示:
到达时间    业务时长    开入时间    预期离开时间    实际离开时间    停留时长
1  3          1           4  9         8 (需要等待第2、3辆车开出才能开出)
2               5          2           7                9               7 (需要等待第3辆车开出才能开出)
3               6          3           9           9           6
4           1          9           10           12           8 (等待第3、2、1辆车依次开出后才能开入,等待第5辆车开出才能开出)
5           3    9        12           12         7 (等待第3、2、1辆车依次开出后才能开入)

测试样例2:

输入
5 3
3 9
12 8
21 25
输出
9
8
25

杨老师的解题思路:按照车辆到达的时间顺序,模拟车辆的进入和离开过程。
初始化:创建一个栈来模拟停车场
对于新到来的每辆车,如果停车场栈是空,则直接进栈:
如果停车场未满,且新车辆的到达时间比栈顶车辆的预期离开时间(其进场时间+停留时间)
要早,也直接进栈:
否则要进行出栈处理,即先处理车辆离开,要注意当前时间的变化,当有车离开时,当前时间变化为离开车辆的进场时间+停留时间,并且一个车出栈后可能紧接着前面进入的车也要
出去。
解题时要注意车辆进入停车场的时间和其到达的时间不一定一样,离开停车场时间也不一定是其进场时间+停留时间。

实话说,这题就是理解很折磨,慢慢做就好了QAQ

#include <iostream>
#include <stack>
using namespace std;
struct car{
	int x;int lt;int i;
};//x表示到达时间,lt表示预计停留时间,i表示车的编号 
int main() {
	int n,m;cin>>n>>m;
	stack<car> s;//栈,表示停车场 
	int wait[m+1];//代表每辆车的等待时间 
	int t=0;//表示当前时间 
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		if(s.empty()){
			if(t<=x) t=x;
			s.push({x,t+y,i});
			//每辆车到达时刻如果比当前时间晚,那么更新当前时间到车驶入时间
			//每辆车到达时刻如果比当前时间早则不作处理  t才是车真正的入库时间 
			//当前时间加上业务时间即为预计离开时间 
		}
		else{
			car dc=s.top();
			if(s.size()!=n&&x<dc.lt){
				if(t<=x) t=x;
				s.push({x,t+y,i});
			}//判断是否当前有车要离开 
			else{
				while(!s.empty()&&(s.size()==n||x>=dc.lt||dc.lt<t)){
					if(t<=dc.lt) t=dc.lt;
					wait[dc.i]=t-dc.x;
					s.pop();
					if(!s.empty()) dc=s.top();
				}
				if(t<=x) t=x;
				s.push({x,t+y,i});
			}
		}
	}//不要忘记也许车库还有车 
	while(!s.empty()){
	car dc=s.top();
	if(t<=dc.lt) t=dc.lt;
	wait[dc.i]=t-dc.x;
	s.pop();if(!s.empty()) dc=s.top();
	}
	for(int i=1;i<=m;i++) cout<<wait[i]<<endl;
    return 0;
}    

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值