课设有这道题,粘个代码,顺便浅聊一下~
Question:
Solve:
既那就由浅到深分析:
读完题,很明显,出票方案只有两种:同排座位或者非同排座位
1.
如果不考虑时间复杂度的问题,那直接开一个 bool 型的二维数组 a[20][5] 来表示每一个座位是否已经购买。对于每次的购票指令,先遍历一次数组,对同排的剩余座位计数,判断能不能出同排的票,如果能的话就直接输出座位,不能的话就再遍历一次数组,按空余座位的顺序出票。
2.
上述方式可以解决问题,但如果数据比较刁钻,遍历两次二维数组多少有点~,所以:
注意到上面标红的位置是一个计数的过程,我们正好可以利用这个计数将二维数组转变为一维, 就是
a[ i ] = x, 表示第 i 排座位当前所剩的座位数为 x
这样做的意义是什么呢?
之前我们需要遍历的是 100 个座位, 而现在需要遍历的就只是 20 排,通过 a[ i ] 与 购票数 的大小直接判断能不能出同排座位的票,能的话就利用数学关系输出座位号,不能的话就再遍历每一排,将座位号输出
时间上会大幅缩减
到这里,需要解决的就只剩一个问题了,通过 row[i] = x 得出座位号:
第 i 排有座的起始位置
= (i - 1)排满座 + 该排已经被买走的票的数量 + 1
= (i - 1) * 5 + ( 5 - row[ i ] ) + 1
至于具体输出座位号,如果你输出座位号的时候循环下标是从 1 开始的,1 就没必要加了,自己调整就行
3.
到此为止,这个问题就已经解决了,去CSP提交会拿满分,我的代码也就到这一步
不过我还是想继续扯两句
(1)一个购票系统,有票无票是基本判断,所以加个整体变量 sum 表示总体的剩余车票数,这很合理,顺便再加个提示
(2)能不能再画蛇添足呢?接下来就是我第一次读题想的鬼优化:
在之前的基础上,为了更快的满足出票需求,我开一个数组 pos[ i ] = x, 表示 第一个有 i 个连续座位的排是第 x 排
如果这样整的话,那基本上就不需要遍历,就直接结合 每一排剩余的座位数 和 pos[ i ],立马就能判断有没有连续座位,而且座位号也是非常容易计算出来
难点在哪呢?在数据更新上,在每一次出完票以后,需要更新所有 pos[ i ] 。如果当前排的连续座位数不够 i 个,就让pos[ i ]++,指向下一排,如果 pos[ i ] > 最大排数,就说明没连续 i 个座位的票了
不说了,读者可以顺着这个思路写一写,挺那啥的~
AC Code:
以下为同源代码,三种语言套共用一个内核,不想整活了
C++
#include <iostream>
using namespace std;
#define N 20
int n, p, exist_ticket[N+1];
void init() //初始化
{
std::ios::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
//初始化每一排的剩余座位数
for(int i = 1; i <= N; i++) exist_ticket[i] = 5;
}
void solve(){ //出票
//有连续座位
for(int i = 1; i <= N; i++){
if(exist_ticket[i] < p) continue;
//计算座位号
int x = (i-1)*5 + 5 - exist_ticket[i];
//输出结果 更新数据
for(int j = 1; j <= p; j++){
cout <<x+j <<" ";
exist_ticket[i]--;
}
//得到结果,进行下一次出票
cout <<endl; return;
}
//无连续座位(顺序取票)
for(int i = 1; i <= N && p; i++){
if(!exist_ticket[i]) continue; //该排无票
//计算座位号
int x = (i-1)*5 + 5 - exist_ticket[i];
int minn = min(p, exist_ticket[i]);
//输出结果 更新数据
for(int j = 1; j <= minn; j++){
cout <<x+j <<" ";
p--, exist_ticket[i]--;
}
}
cout <<endl;
}
int main(void)
{
init(); //初始化
cin >>n;
while(n--){
cin >>p; solve();
}
return 0;
}
Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
//初始化座位信息
int row[] = new int[21];
for(int i = 1; i <= 20; i++) row[i] = 5;
int n = cin.nextInt();
while(n != 0){
//标记是否有连续座位
boolean IsAnd = false;
int p = cin.nextInt();
for(int i = 1; i <= 20; i++){
//有连续座位
if(row[i] >= p) {
IsAnd = true;
//计算座位号,出票
int x = (i - 1) * 5 + 5 - row[i];
for (int j = 1; j <= p; j++) {
//直接输出p个座位信息
System.out.print((x + j) + " ");
row[i]--;
}
break;
}
}
//无连续座位
if(IsAnd == false) {
for (int i = 1; i <= 20 && p != 0; i++) {
if (row[i] == 0) continue; //该牌无票
int x = (i - 1) * 5 + 5 - row[i];
//判断该排剩余座位是否需要全部输出
int minn = (p < row[i] ? p : row[i]);
for (int j = 1; j <= minn; j++) {
System.out.print((x + j) + " ");
row[i]--; p--;
}
}
}
System.out.println();
n--;
}
}
}
Python
#input
n = int(input())
p = input().split()
#init
a = [5 for i in range(20)]
#solve
for value in p:
value = int(value)
ans = False
for i in range(20):
#have continue ticket
if a[i] >= value:
x = i * 5 + 5 - a[i]
for j in range(value):
print("%d "%(x + j + 1), end=('\n' if j == value-1 else ''))
a[i] -= 1
ans = True
break
if ans == True:
continue
#opposite
for i in range(20):
if value == 0:
break
x = i*5 + 5 - a[i]
minn = min(a[i], value)
temp = minn - 1
while temp >= 0:
print("%d "%(x + minn - temp), end=('\n' if value == 1 else ''))
value -= 1
a[i] -= 1
temp -= 1
python代码量少实锤,但是我刚用写的有点乱,还调了老长时间...
图片来自CCF官网,侵权请联系删除~