目录
201609-2 火车购票
问题描述 请实现一个铁路购票系统的简单座位分配算法,来处理一节车厢的座位分配。 输入格式 输入的第一行包含一个整数n,表示购票指令的数量。 输出格式 输出n行,每行对应一条指令的处理结果。 样例输入 4 样例输出 1 2 样例说明 1) 购2张票,得到座位1、2。 评测用例规模与约定 对于所有评测用例,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, K。 输出格式 输出一行,包含N个整数,相邻整数间用一个空格分隔,依次表示每个挂钩上挂的钥匙编号。 样例输入 5 2 样例输出 1 4 3 2 5 样例说明 第一位老师从时刻3开始使用4号教室的钥匙,使用3单位时间,所以在时刻6还钥匙。第二位老师从时刻2开始使用钥匙,使用7单位时间,所以在时刻9还钥匙。 样例输入 5 7 样例输出 1 2 3 5 4 评测用例规模与约定 对于30%的评测用例,1 ≤ N, K ≤ 10, 1 ≤ w ≤ N, 1 ≤ s, c ≤ 30; |
思路
解法一
可以将需要进行的操作一次排序。再用数组模拟,遍历每一次操作。
每一个老师都有一次拿钥匙和取钥匙的操作,同时也对应着两个时间节点。我们把每一个时间节点进行的每一个操作记录下来,在排序的时候根据题干要求用自定义的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,表示给定数字的个数。 输出格式 输出多行,每行包含两个整数,分别表示一个给定的整数和它出现的次数。按出现次数递减的顺序输出。如果两个整数出现的次数一样多,则先输出值较小的,然后输出值较大的。 样例输入 12 样例输出 3 4 评测用例规模与约定 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)
题目描述
输入
输入描述:一行输入两个整数n,m(1<=m<=n<=10)
输出
输出描述:按照从小到大的顺序输出所有方案,一行一个方案。
样例输入
3 2
样例输出
1 2
1 3
2 3
提示
可以思考一下如果题干设置成同一数字可以多次使用,我们只需要简单修改一下即可
二叉树的深度
题目描述
题目描述:
给出一棵根节点为1的二叉树,和每个点的左右儿子节点,求这棵二叉树的深度。
输入
输入描述:
第一行输入二叉树的节点数n。 (1<=n<=1000)
接下来n行,每行输入两个整数l,r分别代表左儿子节点编号和右儿子节点编号,0代表节点为空。
输出
输出描述:
输出二叉树的深度
样例输入
5
2 3
0 0
4 5
0 0
0 0
样例输出
3
Acwing823 排列
题目描述
给定一个整数 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题库