信奥赛-刷题笔记-队列篇-T2-P1996约瑟夫和P5661公交换乘

总题单

本部分总题单如下

【腾讯文档】副本-CSP-JS+NOI 题单 (未完待续)

https://docs.qq.com/sheet/DSmJuVXR4RUNVWWhW?tab=BB08J2

队列篇题单

信奥赛-刷题笔记-队列篇-T2-P1996约瑟夫和P5661公交换乘_第1张图片

P1996 约瑟夫问题

https://www.luogu.com.cn/problem/P1996

题目描述

n n n 个人围成一圈,从第一个人开始报数,数到 m m m 的人出列,再由下一个人重新从 1 1 1 开始报数,数到 m m m 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。

注意:本题和《深入浅出-基础篇》上例题的表述稍有不同。书上表述是给出淘汰 n − 1 n-1 n1 名小朋友,而该题是全部出圈。

输入格式

输入两个整数 n , m n,m n,m

输出格式

输出一行 n n n 个整数,按顺序输出每个出圈人的编号。

输入输出样例 #1

输入 #1

10 3

输出 #1

3 6 9 2 7 1 8 5 10 4

说明/提示

1 ≤ m , n ≤ 100 1 \le m, n \le 100 1m,n100

代码1

#include
#include
#include
using namespace std;
queue<int> q;// 队列q 存储每个人 
int main()
{
	// n个人 数到m,这个人就退出 
	int n,m;
	// e表示开始查的数  
	int e=1;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		q.push(i);//模拟队列 把1到n个人插入到队列中 
	}
	// 如果队列不为空 
	while(!q.empty())
	{
		// 如果开始查的数e与m相等 
		if(e==m)//如果这个人正好被踢 
		{
			cout<<q.front()<<" ";//先输出队列队首 
			q.pop();//再删除队首 
			e=1;//再从1开始报数 
		}
		// 如果开始查的数e与m不相等 
		else if(e!=m)//如果不被剔除 
		{
			e++;//报的数+1 
			q.push(q.front());//先把head压进队尾 
			q.pop();//再把head删除 
		}
	}
	return 0;//结束程序(完美) 
}

代码2

#include
using namespace std;
int main()
{
    int n,m,s=0; // s表示当前人的序号从1到n  n表示总人数 m表示数到m就出队
    scanf("%d%d",&n,&m);//入读
    bool visit[200]={0};//visit赋初始值
    for(int k=0;k<n;k++){//总共要出队n次
        for(int i=0;i<m;i++){ //每次出队后都重1开始查数
            if(++s>n)s=1;// 如果s大于n表示喊道了最后一位,s设置为1
            if(visit[s])i--; //如果队列visit[s]为true 则表示该用户已经出队了  执行i--
        }//类似取模,而因为序列是从1开始的,所以不取模,加判断;若visit过,则i--,使其继续循环
        printf("%d ",s);// 循环结束后 输出序号s
        visit[s]=true;//输出,记录s已出队
    }
    return 0;
}

P5661 CSP-J2019 公交换乘

https://www.luogu.com.cn/problem/P5661

题目描述

著名旅游城市 B 市为了鼓励大家采用公共交通方式出行,推出了一种地铁换乘公交车的优惠方案:

  1. 在搭乘一次地铁后可以获得一张优惠票,有效期为 45 分钟,在有效期内可以消耗这张优惠票,免费搭乘一次票价不超过地铁票价的公交车。在有效期内指开始乘公交车的时间与开始乘地铁的时间之差小于等于 45 分钟,即: t b u s − t s u b w a y ≤ 45 t_{bus} - t_{subway} \leq 45 tbustsubway45
  2. 搭乘地铁获得的优惠票可以累积,即可以连续搭乘若干次地铁后再连续使用优惠票搭乘公交车。
  3. 搭乘公交车时,如果可以使用优惠票一定会使用优惠票;如果有多张优惠票满足条件,则优先消耗获得最早的优惠票。

现在你得到了小轩最近的公共交通出行记录,你能帮他算算他的花费吗?

输入格式

输入文件的第一行包含一个正整数 n n n ,代表乘车记录的数量。

接下来的 n n n 行,每行包含 3 个整数,相邻两数之间以一个空格分隔。第 i i i 行的第 1 个整数代表第 i i i 条记录乘坐的交通工具,0 代表地铁,1 代表公交车;第 2 个整数代表第 i i i 条记录乘车的票价 p r i c e i price_i pricei ;第三个整数代表第 i i i 条记录开始乘车的时间 t i t_i ti (距 0 时刻的分钟数)。

我们保证出行记录是按照开始乘车的时间顺序给出的,且不会有两次乘车记录出现在同一分钟。

输出格式

输出文件有一行,包含一个正整数,代表小轩出行的总花费。

输入输出样例 #1

输入 #1

6
0 10 3
1 5 46
0 12 50
1 3 96
0 5 110
1 6 135

输出 #1

36

输入输出样例 #2

输入 #2

6
0 5 1
0 20 16
0 7 23
1 18 31
1 4 38
1 7 68

输出 #2

32

说明/提示

样例 1 说明

第一条记录,在第 3 分钟花费 10 元乘坐地铁。

第二条记录,在第 46 分钟乘坐公交车,可以使用第一条记录中乘坐地铁获得的优惠票,因此没有花费。

第三条记录,在第 50 分钟花费 12 元乘坐地铁。

第四条记录,在第 96 分钟乘坐公交车,由于距离第三条记录中乘坐地铁已超过 45 分钟,所以优惠票已失效,花费 3 元乘坐公交车。

第五条记录,在第 110 分钟花费 5 元乘坐地铁。

第六条记录,在第 135 分钟乘坐公交车,由于此时手中只有第五条记录中乘坐地铁获得的优惠票有效,而本次公交车的票价为 6 元,高于第五条记录中地铁的票价 5 元,所以不能使用优惠票,花费 6 元乘坐公交车。

总共花费 36 元。

样例 2 说明

第一条记录,在第 1 分钟花费 5 元乘坐地铁。

第二条记录,在第 16 分钟花费 20 元乘坐地铁。

第三条记录,在第 23 分钟花费 7 元乘坐地铁。

第四条记录,在第 31 分钟乘坐公交车,此时只有第二条记录中乘坐的地铁票价高于本次公交车票价,所以使用第二条记录中乘坐地铁获得的优惠票。

第五条记录,在第 38 分钟乘坐公交车,此时第一条和第三条记录中乘坐地铁获得的优惠票都可以使用,使用获得最早的优惠票,即第一条记录中乘坐地铁获得的优惠票。

第六条记录,在第 68 分钟乘坐公交车,使用第三条记录中乘坐地铁获得的优惠票。

总共花费 32 元。

数据规模与约定

对于 30 % 30\% 30% 的数据, n ≤ 1000 n \leq 1000 n1000 t i ≤ 1 0 6 t_i \leq 10^6 ti106

另有 15 % 15\% 15% 的数据, t i ≤ 1 0 7 t_i \leq 10^7 ti107 ,所有 p r i c e i price_i pricei 相等。

另有 15 % 15\% 15% 的数据, t i ≤ 1 0 9 t_i \leq 10^9 ti109 ,所有 p r i c e i price_i pricei 相等。

对于 100 % 100\% 100% 的数据, n ≤ 1 0 5 n \leq 10^5 n105 t i ≤ 1 0 9 t_i \leq 10^9 ti109 1 ≤ p r i c e i ≤ 1000 1 \leq price_i \leq 1000 1pricei1000

代码

#include 

using namespace std;
const int MAXN = 100005;
struct Ticket {
    //赠票的价格,最晚使用时间和是否需用过
    int price, time, used;
} q[MAXN];//赠票盒子
int head, tail, n, cost;

int main() {
    cin >> n;
    for (int i = 0; i < n; ++i) {
        int op, price, time;
        //输入每次坐车的种类,价格和发车时间
        cin >> op >> price >> time;
        if (op == 0) {
            //如果是坐地铁,直接把价格加到cost里面
            cost += price;
            //新一张赠票插入数组末尾,这张票的最晚使用时间是当前时间+45
            q[tail].time = time + 45;
            //赠票面额就是地铁票价
            q[tail++].price = price;
        } else {
            //先用一个循环把过期票扔掉
            while (head < tail && q[head].time < time) {
                head++;
            }
            bool found = false;//表示是否有合适的赠票,先假设没有
            for (int j = head; j < tail; ++j) {
                //循环所有剩余的票,这些一定都没过期,因为题目中时间是按顺序给我们的
                if (q[j].price >= price && q[j].used == 0) {
                    //如果价格合适,并且没用过,标记找到了,这张票标记用过
                    found = true;
                    q[j].used = 1;
                    break;
                }
            }
            //如果没找到合适的赠票,老老实实花钱买吧
            if (!found) cost += price;
        }
    }
    cout << cost << endl;
    return 0;
}

你可能感兴趣的:(信奥赛-刷题篇,笔记)