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;
}