窗口排队模拟
eg1. 1017 Queueing at Bank (25 分)
题意:给出顾客数与窗口数,以及顾客的到达时间和处理时间;Bank的工作时间在8.-17.;在8.前到达需要等到8.后才开始办理,17.点及之后不办理,88了;计算平均等待时间;
思路:
- 输入;
- 按照到达时间排序;
- 先将空窗口填满(入队列):即到即办理,或者等到8点,计算结束时间和等待时间;
1
2
3
4
5
6
7
8
9//在8.以前到达
if(Start>Customer[i].arr_time){
Customer[i].wait_time=Start-Customer[i].arr_time;
Customer[i].finish_time=Customer[i].p_time;
}
else {
Customer[i].wait_time=0;
Customer[i].finish_time=Customer[i].arr_time+Customer[i].p_time-Start;
} - 然后选取办理时间最早结束的出队列:它的结束时间与下一个候选顾客的到达时间作比较,到达之后就有空位置即办理,或者等到最先结束的完成后, 计算结束时间和等待时间;
1
2
3
4
5
6
7//等待时间为前一个结束时间-到达时间
//或者直接有空窗口,无需等待
int temp=n.finish_time+Start-Customer[i].arr_time;
if(temp>0){
Customer[i].wait_time=temp;
}
else Customer[i].wait_time=0; - 3、4步维护总等待时间,当当前顾客到达时间>=17.,结束啦!
注意点:
- 时间以s统一界定,以免逻辑上把自己给绕晕;
- 构造数据结构node,存放每个顾客的到达时间,等待时间,结束时间,以及处理时间;
- 构造优先队列priority_queue,重载<号,结束时间早的优先;
1
2
3
4
5
6
7
8
9
10
11
12struct node
{
int arr_time;
int wait_time;//以s计算的等待时间
int finish_time,p_time;//以s计算的结束时间与处理时间
friend bool operator <(const node&a,const node&b){
return a.finish_time>b.finish_time;
};
}Customer[10005];
priority_queue <node> Lines;eg2.1014 Waiting in Line (30 分)
题意:输入窗口数,黄线内每个队伍的最大人数,顾客数与查询数;输入顾客的处理时间,8.开始处理,17.后不接受处理,计算每个顾客处理结束的时间;
思路:
- 构造顾客数据结构,存放处理时间与结束时间;
- queue
[i]作为窗口,存放在黄线内的顾客; 1
2
3
4
5
6
7
8
9
10
11
12struct node
{
int pro_time;
int fin_time;
node(int _p,int _f)
{
pro_time=_p;
fin_time=_f;
}
};
queue<node> Lines[25]; - flag_time[25]数组表示每个窗口队首的结束时间,wf_time[1005]存放K个顾客的结束时间;
- 填满黄线内窗口空位M*N,一旦进入黄线,结束顾客结束时间确定,即队列顾客back的结束时间+当前顾客的处理时间,更新flag_time与wf_time;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16bool ff=true;
for(int i=0;i<M&&ff;i++){
for(int j=0;j<N;j++){
int temp_time;
if(Lines[j].size()==0)temp_time=0;
else temp_time=Lines[j].back().fin_time;
Lines[j].push(node(p_time[index],p_time[index]+temp_time));
flag_time[j]=Lines[j].front().fin_time;
if(temp_time>=540)wf_time[index]=-1;//开始的时间在17.及之后
else wf_time[index]=p_time[index]+temp_time;
index++;
if(index>K){
ff=false;break;
}
}
} - 黄线外顾客进入队列,首先找到flag_time[]中的最小值,因为最小即该队列最先会有人结束,记录窗口值,队首顾客出队,下一个顾客进队,更新flag_time与wf_time;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21for(int i=index;i<K;i++){
//找到flag_time中的最小值
int Min=flag_time[0];
int Min_index=0;
for(int w=1;w<N;w++){
if(flag_time[w]<Min){
Min=flag_time[w];
Min_index=w;
}
}
//第Min_index个窗口最先处理完
int temp_time=Lines[Min_index].back().fin_time;
// cout<<temp_time<<" "<<p_time[index]<<endl;
Lines[Min_index].pop();
Lines[Min_index].push(node(p_time[index],p_time[index]+temp_time));
flag_time[Min_index]=Lines[Min_index].front().fin_time;
if(temp_time>=540)wf_time[index]=-1;//开始的时间在17.及之后
else wf_time[index]=p_time[index]+temp_time;
index++;
}
注意点:
- if-else,当顾客的开始处理时间在17.及之后,直接忽略了;
eg3. 1026 Table Tennis (30 分)
多项式
eg1. 1002 A+B for Polynomials (25 分)
eg2. 1009 Product of Polynomials (25 分)
题意:输入指数与系数,求两个多项式的和(或乘积);按指数从大到小输出非零项;
思路:map<int,double>a,b作为输入的A、B多项式;map<int,double,greater
pair用法:make_pair(x,y);pair->first
map用法:每个key在map中只能出现一次;mp.insert(pair);mp.erase(iter);mp.size();
计算树的叶子结点
eg1. 1004 Counting Leaves (30 分)
题意:家谱树,计算每层没有孩子的成员,即计算树的每层叶子结点树;输入树中结点总数N,以及叶子结点数M;输出M个非叶子结点的K个孩子;输出各层叶子结点的个数;
思路:
- 数据结构树的结点使用vector存放孩子列表,层数以及当前index;
- 树的BFS queue;依次将树结点压入队列,同一层的拥有一样的父母结点,都会挤在一起;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20void Layer_Tra()
{
queue<node> q;
int leavenum=0;
q.push(Family[1]);
while(!q.empty()){
node n=q.front();
q.pop();
if(n.children.size()==0){
leaves[n.level]++;
}
now_level=n.level;
// cout<<now_level<<endl;
for(int i=0;i<n.children.size();i++){
Family[n.children[i]].level=n.level+1;
//node temp=Family[n.children[i]];
q.push(Family[n.children[i]]);
}
}
}eg2. 1094 The Largest Generation (25 分)
题意:同上题,计算的是有最多成员的成员值以及层数;
思路:同样是使用BFS,用level更新孩子们所在层数,同时用数组[parent.level+1]来维护该层结点的个数;
计算最佳路径(图的DFS)
eg1. 1003 Emergency (25 分)
题意:输入城市数N、路径数M、in and save city,N个城市的rescue数,M条路径;找出最短路径的长度,以及在路上可以收集的最大rescue teams;输出最短路径的数量以及最大值;
思路:
- 构建一张有权无向图,求路径最短及节点上值和最大的路径;
- DFS(start,end)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15if(index==End){
...
return;
}
for(int i=0;i<N;i++){
if(Map[index][i]!=0&&flag[i]==false){
flag[i]=true;
length+=Map[index][i];
rescueSum+=rescueNum[i];
DFS(i,End);
flag[i]=false;
length-=Map[index][i];
rescueSum-=rescueNum[i];
}
}
注意点:
1.在进入DFS函数前初始化,将起点flag[C1]置为true等:
1
2
3rescueSum+=rescueNum[C1];
flag[C1]=1;
DFS(C1,C2);
时间的比较
eg. 1006 Sign In and Sign Out (25 分)
题意:输入记录:ID+到达时间+离开时间,分别找到最早到与最晚离开的同学,输出ID;
思路:
pair的妙用:first存时间,second存学号;max与min比较的是first字段;时间字符串直接按字典序比较;
1 | typedef pair<string,string> tlog; |
大数+二分查找
题意:输入2个数以及其中一个数的进制,找到使这两个数相等的另一个数的进制;
思路:
- 字符串输入,转换成10进制比较;
- 二分查找;
注意点:
- 转换成10进制时,有可能溢出,需要判断;
- 二分查找的范围是当前值各个位数中的最大值+1,已知进制值的这个数+1(不可能比这个数更大了,因为这时表示为10);
数据结构
Stack
1051 Pop Sequence (25 分)
判断pop序列是否正确
思路:
- 模拟1~N的入栈,以current指向序列首位;遇到相同的则出栈,current后移;
- 当超出栈容量或者全入栈后,current不为N时,则序列不正确;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44#include <iostream>
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <string.h>
using namespace std;
int main(){
int M,N,K;// stack的最大容量,push长度,数量
vector<int> popList;// pop序列
vector<int> myStack;// 栈
scanf("%d%d%d",&M,&N,&K);
while(K--){
int num;
popList.clear();
myStack.clear();
// 输入pop序列
for(int i=0;i<N;i++){
scanf("%d", &num);
popList.push_back(num);
}
int current=0;// 指向popList当前位置
bool flag=true;// 标记
for(int i=1;i<=N;i++){
myStack.push_back(i);// 入栈
// 超过最大容量
if(myStack.size()>M){
flag=false;
break;
}
// 当发现当前栈元素与popList头部元素相似
while(!myStack.empty()&&myStack.back()==popList[current]){
myStack.pop_back();
current++;
}
}
//当popList没有遍历完全或者超出最大容量
if(current!=N||flag==false){
cout<<"NO"<<endl;
}
else cout<<"YES"<<endl;
}
return 0;
}Queue
1056 Mice and Rice (25 分)
思路:
- 题目看了很久才理解;
- 使用一个队列存放参赛者名单,pop首部来模拟小组比赛,将获胜者push到队列尾巴,知道队列中只剩一个笑到最后的玩家;
- 注意在模拟每一局比赛时,固定初始队列大小,而不是动态获取;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67#include <iostream>
#include <stdio.h>
#include <queue>
#include <algorithm>
#include <string.h>
using namespace std;
const int MAX=1010;
struct node
{
int Weight;
int Rank;
int Pos;
}Input[MAX];
int main(){
int NP,NG;//参赛者数,小组人数
queue<node> mice;// 存放参加比赛的老鼠
cin>>NP>>NG;
for(int i=0;i<NP;i++){
cin>>Input[i].Weight;
Input[i].Pos=i;
}
//将初始比赛序列放入队列
int p;
for(int i=0;i<NP;i++){
cin>>p;
mice.push(Input[p]);
}
while(!mice.empty()){
//当队列中只剩一个
if(mice.size()==1){
node temp=mice.front();
Input[temp.Pos].Rank=1;
break;
}
int groupNum=mice.size()/NG;//比赛数
if(mice.size()%NG!=0)groupNum+=1;//剩余的归于一组
int oneTimeNum=mice.size();//记录本轮参与者数量
//直接使用mice.size()会在一组比赛结束后发生变化(得到获胜者,插入到队列中后
for(int i=0;i<groupNum;i++){
int winPosition;
int maxW=-1;
int memberNum=NG;
// 最后一组特殊情况考虑
if(i==groupNum-1&&oneTimeNum%NG!=0){
memberNum=oneTimeNum%NG;
}
for(int j=0;j<memberNum;j++){
node temp=mice.front();
mice.pop();
if(maxW<temp.Weight){
maxW=temp.Weight;
winPosition=temp.Pos;
}
Input[temp.Pos].Rank=groupNum+1;
}
mice.push(Input[winPosition]);
}
}
// 输出
for(int i=0;i<NP;i++){
if(i!=0)cout<<" ";
cout<<Input[i].Rank;
}
return 0;
}LinkList
1052 Linked List Sorting (25 分)
思路:
- 按order排序,按序输出;
- 有效node的order为key值,无效node的order设为最大值,则在排序时会排到后面去;
- 注意:需要对原链表遍历处理,清除无效结点;当有效结点值为空的输出;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53#include <iostream>
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <string.h>
using namespace std;
const int MAX = 100010;
struct node {
int add,key,next;
int order;
}myList[MAX];
int N,head;
bool cmp(node a,node b)
{
return a.order<b.order;
}
int main(){
// 初始化,使空结点值排到后面
for(int i=0;i<MAX;i++){
myList[i].order=MAX;
}
scanf("%d%d",&N,&head);
// 输入N个结点
int address,key,nextadd;
for(int i=0;i<N;i++){
scanf("%d%d%d",&address,&key,&nextadd);
myList[address].add=address;
myList[address].key=key;
myList[address].next=nextadd;
}
int temp=head;
int vaild=0;
while(temp!=-1){
vaild++;
myList[temp].order=myList[temp].key;
temp=myList[temp].next;
}
N=vaild;
if(N==0){
printf("%d %d\n",N,-1);
return 0;
}
sort(myList,myList+MAX,cmp);
printf("%d %05d\n",N,myList[0].add);
for(int i=0;i<N-1;i++){
printf("%05d %d %05d\n",myList[i].add,myList[i].key,myList[i+1].add);
}
printf("%05d %d %0d\n",myList[N-1].add,myList[N-1].key,-1);
return 0;
}
贪心
1033 To Fill or Not to Fill (25 分)
1 | #include <iostream> |
1067 Sort with Swap(0, i) (25 分)
1 | #include <iostream> |
树
二叉树的遍历
1020 Tree Traversals (25 分)
1086 Tree Traversals Again (25 分)
已知树的中序遍历、后序(先序)遍历,建树;
思路:(以先序为例)
- 先序遍历的首位结点为当前数的根结点;
- 在中序遍历中找到首位结点的位置,将该树分为左子树与右子树2部分;
- 递归求左右子树,直到当前结点为空;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75#include <iostream>
#include <stdio.h>
#include <vector>
#include <queue>
#include <algorithm>
#include <string.h>
using namespace std;
vector<int>preOrder,inOrder,tempOrder,postOrder;
int N;
struct node
{
int data;
node* left;
node* right;
};
node* CreateTree(int prel,int prer,int inl,int inr)
{
if(prel<=prer){
int k;
//postr即为当前根节点值
//找到中序排列中根节点的位置
for(k=inl;k<=inr;k++){
if(inOrder[k]==preOrder[prel]){
break;
}
}
//左子树的结点值
int numLeft=k-inl;
node* root=new node;
root->data=inOrder[k];
root->left=CreateTree(prel+1,prel+numLeft,inl,k-1);
root->right=CreateTree(prel+numLeft+1,prer,k+1,inr);
return root;
}
return NULL;
}
//后序遍历
void postTraversals(node* root)
{
if(root==NULL)return;
postTraversals(root->left);
postTraversals(root->right);
postOrder.push_back(root->data);
}
int main(){
int input;
char oper[5];
cin>>N;
for(int i=0;i<2*N;i++){
getchar();
scanf("%s",&oper);
if(!strcmp(oper,"Push")){
cin>>input;
preOrder.push_back(input);
tempOrder.push_back(input);
}
else {
int in=tempOrder.back();
tempOrder.pop_back();
inOrder.push_back(in);
}
}
node* root=CreateTree(0,N-1,0,N-1);
postTraversals(root);
for(int i=0;i<N;i++){
if(i!=0)cout<<" ";
cout<<postOrder[i];
}
return 0;
}树的遍历
1079 Total Sales of Supply Chain (25 分)
思路:DFS
- 求供应链中产品从供应商到零售商流动后,得到最终的总价格;
- 即DFS遍历求每个叶子结点的深度;
1090 Highest Price in Supply Chain (25 分)The Largest Generation (25 分)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52#include <iostream>
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <string.h>
#include <math.h>
using namespace std;
struct node
{
int products;
vector<int> child;
}supplyChain[100001];
int N;//结点数
double P,r;//根节点的初始价格,每层增加的百分比
double sum=0;
void DFS(int root,int level)
{
if(supplyChain[root].child.size()==0){//到达根节点
sum+=pow(r,level)*supplyChain[root].products*P;
return;
}
for(int i=0;i<supplyChain[root].child.size();i++){
DFS(supplyChain[root].child[i],level+1);
}
}
int main(){
scanf("%d%lf%lf",&N,&P,&r);
r=(double)1+r/100;
int k,input;
//输入
for(int i=0;i<N;i++){
cin>>k;
if(k==0){
cin>>supplyChain[i].products;
}
else {
for(int j=0;j<k;j++){
cin>>input;
supplyChain[i].child.push_back(input);
}
}
}
//DFS求层数
DFS(0,0);
printf("%.1lf",sum);
return 0;
}
求拥有最大结点数的level
1004 Counting Leaves (30 分)
求树的每层叶子结点数,可用DFS或BFS;
思路:(BFS)
- 使用一个queue做BFS,一个level[i]数组存储i结点所在的层数,一个res[i]数组存储i层的叶子节点数;
- pop出当前结点;for循环得出当前结点的孩子结点,此时设置孩子结点的level为当前结点level+1;
- 当当前结点孩子数为空,则为叶子结点,res[level]++;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55#include <iostream>
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <queue>
using namespace std;
int N,M;//总结点数,非叶子结点数
vector<int> myTree[105];
int res[105]={0};
int level[105]={0};//各结点所处的层号
int maxlevel=-1;
void BFS(int root)
{
queue<int>myQueue;
myQueue.push(root);
while(!myQueue.empty()){
int temp=myQueue.front();
myQueue.pop();
maxlevel=max(maxlevel,level[temp]);
if(myTree[temp].size()==0){
res[level[temp]]++;
}
for(int i=0;i<myTree[temp].size();i++){
level[myTree[temp][i]]=level[temp]+1;
myQueue.push(myTree[temp][i]);
}
}
}
int main(){
cin>>N>>M;
int id,k;//结点id,子节点数
int input;
memset(res,0,sizeof(res));
for(int i=0;i<M;i++){
cin>>id>>k;
for(int j=0;j<k;j++){
cin>>input;
myTree[id].push_back(input);
}
}
level[1]=1;
BFS(1);
for(int i=1;i<=maxlevel;i++){
if(i!=1)cout<<" ";
cout<<res[i];
}
return 0;
}二叉搜索树BST
1043 Is It a Binary Search Tree (25 分)
判断给定序列是否为BST,或者BST的镜像;
思路:
- 边输入值,边构建二叉搜索树,判断当前插入在root的左子树或右子树;
- 构建完树后进行先序遍历与镜像先序遍历,判断是否与输入初始序列相同;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120#include <iostream>
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <queue>
using namespace std;
int N;
struct node
{
int data;
node* left;
node* right;
};
node* myRoot=NULL;
int original[1005],pre[1005],preMirror[1005],post[1005],postMirror[1005];
int iindex=0;
void CreatBST(node* &root,int data)
{
if(root==NULL){//当根结点为空
root=new node;
root->data=data;
root->left=NULL;
root->right=NULL;
return;
}
if(data>=root->data){//插入到右子树中
CreatBST(root->right,data);
}
else {//插入到左子树
CreatBST(root->left,data);
}
}
void preOrder(node* root)
{
if(root==NULL){
return;
}
pre[iindex++]=root->data;
preOrder(root->left);
preOrder(root->right);
}
void preMirrorOrder(node* root)
{
if(root==NULL){
return;
}
preMirror[iindex++]=root->data;
preMirrorOrder(root->right);
preMirrorOrder(root->left);
}
bool isSame(int a[],int b[])
{
for(int i=0;i<N;i++){
if(a[i]!=b[i])return false;
}
return true;
}
void postOrder(node* root)
{
if(root==NULL){
return;
}
postOrder(root->left);
postOrder(root->right);
post[iindex++]=root->data;
}
void postMirrorOrder(node* root)
{
if(root==NULL){
return;
}
postMirrorOrder(root->right);
postMirrorOrder(root->left);
postMirror[iindex++]=root->data;
}
int main(){
cin>>N;
int data;
for(int i=0;i<N;i++){
cin>>data;
CreatBST(myRoot,data);
original[i]=data;
}
//先序遍历
preOrder(myRoot);
//镜像BST的先序遍历
iindex=0;
preMirrorOrder(myRoot);
if(isSame(original,pre)){
cout<<"YES"<<endl;
iindex=0;
postOrder(myRoot);
for(int i=0;i<N;i++){
if(i!=0)cout<<" ";
cout<<post[i];
}
}
else if(isSame(original,preMirror)){
cout<<"YES"<<endl;
iindex=0;
postMirrorOrder(myRoot);
for(int i=0;i<N;i++){
if(i!=0)cout<<" ";
cout<<postMirror[i];
}
}
else cout<<"NO"<<endl;
return 0;
}完全二叉排序树CBST
1064 Complete Binary Search Tree (30分)
构建一棵CBST
思路:CBST的特点:如果使用数组存放完全二叉树(即层序存放的结果),那么对于x结点,其左孩子结点为2x,右孩子结点为2x+1(假设存在);
1 | #include <iostream> |
平衡二叉树AVL
1 | #include <iostream> |
并查集
1 | #include <iostream> |
1 | #include <iostream> |
- 大数+二分查找
模拟题:
考虑数据结构
一般思路:从一般到特殊