1.201709-2
问题描述
钥匙盒一共有 N个挂钩,从左到右排成一排,用来挂 N个教室的钥匙。一串钥匙没有固定的悬挂位置,但钥匙上有标识,所以老师们不会弄混钥匙。
每次取钥匙的时候,老师们都会找到自己所需要的钥匙将其取走,而不会移动其他钥匙。每次还钥匙的时候,还钥匙的老师会找到最左边的空的挂钩,将钥匙挂在这个挂钩上。如果有多位老师还钥匙,则他们按钥匙编号从小到大的顺序还。如果同一时刻既有老师还钥匙又有老师取钥匙,则老师们会先将钥匙全还回去再取出。
今天开始的时候钥匙是按编号从小到大的顺序放在钥匙盒里的。有 K位老师要上课,给出每位老师所需要的钥匙、开始上课的时间和上课的时长,假设下课时间就是还钥匙时间,请问最终钥匙盒里面钥匙的顺序是怎样的?
接下来 K行,每行三个整数 w, s, c,分别表示一位老师要使用的钥匙编号、开始上课的时间和上课的时长。可能有多位老师使用同一把钥匙,但是老师使用钥匙的时间不会重叠。
保证输入数据满足输入格式,你不用检查数据合法性。
4 3 3
2 2 7
每个关键时刻后的钥匙状态如下(X表示空):
时刻2后为1X345;
时刻3后为1X3X5;
时刻6后为143X5;
时刻9后为14325。
1 1 14
3 3 12
1 15 12
2 7 20
3 18 12
4 21 19
5 30 9
对于60%的评测用例,1 ≤ N, K ≤ 50,1 ≤ w ≤ N,1 ≤ s ≤ 300,1 ≤ c ≤ 50;
对于所有评测用例,1 ≤ N, K ≤ 1000,1 ≤ w ≤ N,1 ≤ s ≤ 10000,1 ≤ c ≤ 100。
在这里,博主介绍两种思路给大家。
前期准备工作做完,我们开始模拟操作。取操作即为遍历数组,当给定数字等于数组中的某个值时,将数组该位置置0即可,注意,这里的查找操作不可以用二分或者其他,因为数字的顺序等因素都是不确定的。存操作即为找到数组中第一个为0的值,按照钥匙编号大小顺序,存入即可。
事实证明楼主的第一种想法实在复杂,楼主写了比较久的时间--|||,同时也提醒大家,A题目时有一个好的逻辑思路是有多么重要!!!下面给大家介绍另外一种比较简便的思路。
我们重新阅读题目可以发现,题目中出现了较多比较操作的暗示,比如按照钥匙标号还,先还后取等,而且我们也是按照时间的先后顺序执行取存操作的,所以这道题可不可以考虑用排序的思路来做呢?我们可以试一试,按照题目的要求,假如我们用结构体数组排序,那么首先肯定按start_time即开始时来排序,那么如果时间相同,对于取操作来说没有影响,存操作就要按照w的先后顺序来执行,这里我们又得出一个排序的依据,那么既有取操作,又有存操作的时候,该怎么办呢?我们很容易想到可以设置一个优先级变量来区别取和存,0和1是在合适不过啦!综上,我们得出一下结论:
(1)第一排序,按照start_time;每一个老师的取操作都对应着一个存操作,所以会有2*K个操作及start_time。
(2)第二排序,按照存取优先级。
(3)第三排序:钥匙的序号大小。
接下来,遍历结构体进行存取即可啦!
第一种思路代码如下:
#include<bits/stdc++.h>
using namespace std;
#define MAX 2010
int key[MAX];
int N;
struct Teachers
{
int w,s,c;
bool ans;
} Teacher[MAX]= {0,0,0,false};
void Store(vector<int> p)
{
int a[MAX],t,num;
num=0;
for(int i=0; i<p.size(); i++)
{
t=p[i];
a[i]=Teacher[t].w;
}
t=p.size();
sort(a,a+t);//按照钥匙编号还
for(int i=0; i<N; i++)
{
if(key[i]==0)
{
key[i]=a[num];
num++;
}
if(num==t) break;
}
}
void Get(vector<int> q)
{
for(int i=0;i<q.size();i++)
{
int t=q[i];
int num=Teacher[t].w;
for(int i=0;i<N;i++)
{
if(key[i]==num)
{
key[i]=0;
break;
}
}
}
}
int main()
{
vector<int>p,q;
int K;
int s_count=0;
int start_time;
scanf("%d%d",&N,&K);
for(int i=0; i<N; i++) key[i]=i+1;
for(int i=0; i<K; i++)
{
scanf("%d%d%d",&Teacher[i].w,&Teacher[i].s,&Teacher[i].c);
if(i==0)
{
start_time=Teacher[0].s;
continue;
}
if(start_time>Teacher[i].s) start_time=Teacher[i].s;
}
do{
p.clear();//p用来存储取操作对应的结构体编号
q.clear();//q用来存储存操作对应的结构体编号
for(int i=0; i<K; i++)
{
if(!Teacher[i].ans&&Teacher[i].s==start_time)
{
p.push_back(i);
Teacher[i].ans=true;
}
else if((Teacher[i].s+Teacher[i].c==start_time)&&Teacher[i].ans)
{
q.push_back(i);
s_count++;
}
}
start_time++;
if(!q.empty()) Store(q);
if(!p.empty()) Get(p);
}while(s_count!=K);//钥匙全部还完即可
for(int i=0; i<N; i++) printf("%d ",key[i]);
return 0;
}
第二组思路的代码如下:
#include<bits/stdc++.h>
using namespace std;
#define MAX 2010//注意MAX的取值,不可以取2000一下。
int key[MAX];
struct Teacher
{
int w;
int cost;
int ans;
}teacher[MAX];
bool cmp(struct Teacher a,struct Teacher b)
{
if(a.cost!=b.cost) return a.cost<b.cost;
else if(a.ans!=b.ans) return a.ans>b.ans;
else return a.w<b.w;
}
int main()
{
int N,K;
scanf("%d%d",&N,&K);
for(int i=0;i<N;i++) key[i]=i+1;
K=K*2;
for(int i=0;i<K;i++)
{
int w,s,c;
scanf("%d%d%d",&w,&s,&c);
teacher[i].w=w;
teacher[i].cost=s;
teacher[i].ans=0;
i++;
teacher[i].w=w;
teacher[i].cost=s+c;
teacher[i].ans=1;
}
sort(teacher,teacher+K,cmp);
for(int i=0;i<K;i++)
{
if(!teacher[i].ans)
{
for(int j=0;j<N;j++) if(key[j]==teacher[i].w) key[j]=0;
}
else
{
for(int j=0;j<N;j++)
if(key[j]==0)
{
key[j]=teacher[i].w;
break;
}
}
}
for(int i=0;i<N;i++) printf("%d ",key[i]);
return 0;
}
2.201709-3
* 字符串 (string):字符串是由双引号 " 括起来的一组字符(可以为空)。如果字符串的内容中出现双引号 ",在双引号前面加反斜杠,也就是用 \" 表示;如果出现反斜杠 \,则用两个反斜杠 \\ 表示。反斜杠后面不能出现 " 和 \ 以外的字符。例如:""、"hello"、"\"\\"。
* 对象 (object):对象是一组键值对的无序集合(可以为空)。键值对表示对象的属性,键是属性名,值是属性的内容。对象以左花括号 { 开始,右花括号 } 结束,键值对之间以逗号 , 分隔。一个键值对的键和值之间以冒号 : 分隔。键必须是字符串,同一个对象所有键值对的键必须两两都不相同;值可以是字符串,也可以是另一个对象。例如:{}、{"foo": "bar"}、{"Mon": "weekday", "Tue": "weekday", "Sun": "weekend"}。
除了字符串内部的位置,其他位置都可以插入一个或多个空格使得 JSON 的呈现更加美观,也可以在一些地方换行,不会影响所表示的数据内容。例如,上面举例的最后一个 JSON 数据也可以写成如下形式。
{
"Mon": "weekday",
"Tue": "weekday",
"Sun": "weekend"
}
给出一个 JSON 格式描述的数据,以及若干查询,编程返回这些查询的结果。
接下来 n 行,描述一个 JSON 数据,保证输入是一个合法的 JSON 对象。
接下来 m 行,每行描述一个查询。给出要查询的属性名,要求返回对应属性的内容。需要支持多层查询,各层的属性名之间用小数点 . 连接。保证查询的格式都是合法的。
如果查询结果是一个字符串,则输出 STRING <string>,其中 <string> 是字符串的值,中间用一个空格分隔。
如果查询结果是一个对象,则输出 OBJECT,不需要输出对象的内容。
如果查询结果不存在,则输出 NOTEXIST。
{
"firstName": "John",
"lastName": "Smith",
"address": {
"streetAddress": "2ndStreet",
"city": "NewYork",
"state": "NY"
},
"esc\\aped": "\"hello\""
}
firstName
address
address.city
address.postal
esc\aped
OBJECT
STRING NewYork
NOTEXIST
STRING "hello"
m ≤ 100,每个查询的长度不超过 80 个字符。
字符串中的字符均为 ASCII 码 33-126 的可打印字符,不会出现空格。所有字符串都不是空串。
所有作为键的字符串不会包含小数点 .。查询时键的大小写敏感。
50%的评测用例输入的对象只有 1 层结构,80%的评测用例输入的对象结构层数不超过 2 层。举例来说,{"a": "b"} 是一层结构的对象,{"a": {"b": "c"}} 是二层结构的对象,以此类推。
考点:
字符串的处理
易错:
字符串读取函数,如getchar(),cin.get(),getline(cin,s),等等
思路:
首先我们要理清题目的意思。题中告诉大家两种格式,一种是字符串,一种是对象,对象中还可以有嵌套的存在。看过样例后,我们首先要解决的问题是,如何存储这些字符呢?当然,首选string类型变量存储,因为string类型操作多而且简便啊!
再看看题目的输入输出描述,输入一个字符串要求输出对应字符串,问题来可,这要怎么办?熟悉STL的同学们肯定反应很快,用map<string,string>容器不就可以了吗?那么现在我们唯一的问题就是,如何存储对应字符串到map容器中了。
我们同样还是按照特殊到一般的思路,请看下面样例:
3 1
{
"firstName": "John"
}
firstName
我们逐行读取,对于第一个'{',我想大部分读者开始的想法是用getchar()吸收这个字符,是的,楼主一开是也是这么想的,吸收过后n必须减1。但是大家仔细读题后可以发现,输入的对象的格式可以很随意,如上述式子也可以表示成:
{"firstName": "John"}
firstName
那么这样一来,吸收掉'{'字符之后,到底应该读取n行,还是n--读取n行呢?吸收掉‘{’字符后很难界定,所以我们选择不吸收,直接处理即可,那么怎么处理呢?请继续向下看~。
回归正题,当我们读取到"firstName": "John",对这个字符串怎么处理。我们可以把它分为两快,一块是键,另一块是值,我们需要一个变量来区分这两者,那么就设置一个bool变量吧。bool变量何时为true何时为false呢?回到"firstName": "John"本身,但我们测试到“““字符时我们开始接收字符,读取到另一个“““时,停止接受。读取到“:“时,这时候读取的值就为value啦。重复上述"""操作即可把字符接受完毕。分析到这里我们发现规律了:
(1)双引号为接收字符,停止接收字符的标志。
(2)“:”冒号为bool值改变的标志。
这仅仅只是一个需要读取的样例,当{}中的样例变多的时候,我们还要处理"{","}"以及","。接下来就看题目给出的样例我们要怎么分析。如下:
10 5
{
"firstName": "John",
"lastName": "Smith",
"address": {
"streetAddress": "2ndStreet",
"city": "NewYork",
"state": "NY"
},
"esc\\aped": "\"hello\""
}
firstName
address
address.city
address.postal
esc\aped
我们假设key存储前面的键值,value读取每一行,我们还有一个string a接收".."内的变量
第一行的"{"我们先不管,先看下面一行。第一行有“”、:、,的存在,不难发现,有:时,为读取value,而有逗号的时候读取键,而当键赋值完毕后,读取的value刚好可以存在map容器中,而且,读取完毕之后,是不是有些string变量应该要清空呢?这是第一行我们看出来的规律。我们着重分析第二行。
第二行多了一个'{'字符,这个字符预示着接下来读取的都是嵌套的变量,键的值不能被清空。
第三行,读取完前面部分后,赋值的时候,键值还要多加一个‘.’,这个要怎么和一般的键值写入区分呢?我们上面提到过,读取完一般字符串行,我们都会要清空键对应的存储变量,以及用来读取"...."内的字符串,而对于对象来说,键是不清空的,那么我们刚好可以依据这个来判断是否需要加上‘.’。在这一行里面,读取完后面的value,我们注意到必须清空key的一部分,便于下次赋值给Key,如key=address.streetAddress后,key的值要回到key=address的状态才方便读取下一行,给key赋值,那么自然想到赋值map容器后,key的值要处理一下。如果其中有点的话,调用substr函数即可,如果没有,说明上一次读取的是字符串,清空Key即可。
接下来,还有一个重点字符"}",读取到它时,意味着一层嵌套要结束了,是的,那么这时候要怎么处理bool值和key值呢?我们在之前说过,当遇到','时,我们的bool值就要改变,而'}'后面要不有',',要不就是结束了,所以bool值在这里不改变也是可行的,问题在于key要如何改变呢?
分情况讨论一下,如果是一层嵌套,我们只要清空key就可以了,可如果有两层嵌套呢?key值还必须保留一层嵌套,两者的区别在哪里?对,一层嵌套中key没有'.',而两层嵌套中有,那么我们在可以在设置一个for循环判断一下,是否需要截取,如果不需要,则清空,需要,就截取。
至此,我们可以开始写代码啦!
扩展:
C++ STRING类型:
(1)定义:string+变量名 如:string a;
(2)基本运算
string a,b,c;
c=a+b;//c字符串的值为a b字符串合并后形成的字符。
(3)基本函数
string a;
a=a.substr(0,i);//截取字符串a中0-(i-1)的字符,返回这些字符组成的字符串。
int b=a.size()//返回字符串a中的字符串个数
Getline()函数
常规用法:
string s;
getline(cin,s)//读取一行字符存入s中
(4)map容器。
定义:
map<type,type>name 如:map<int,int>a
赋值:直接赋值即可。
如:a[1]=2,
a["Sun"]=7;
满分代码如下:
#include<bits/stdc++.h>
#include<string>
using namespace std;
map<string,string>json;
bool ans;
string key;
string value;
void storage()
{
//if(value[0]=='{') return; 形式错误
for(int i=0; i<value.size(); i++)
{
if(value[i]=='{')
{
json[key]="OBJECT";
ans=true;
}
else if(value[i]==':') ans=false;
else if(value[i]==',') ans=true;
else if(value[i]=='}')
{
int j;
for(j=key.size()-1; j>=0; j--)
if(key[j]=='.') break;
if(j>0) key=key.substr(0,j);
else key="";
}
else if(value[i]=='"')
{
string a;
for(i=i+1; i<value.size(); i++)
{
if(value[i]=='\\')
{
i++;
a+=value[i];
}
else if(value[i]=='"') break;
else a+=value[i];
}
if(ans)
{
if(key=="") key=a;
else key+='.'+a;
}
else
{
int j;
json[key]="STRING "+a;
for(j=key.size()-1; j>=0; j--)
if(key[j]=='.') break;
if(j>0) key=key.substr(0,j);
else key="";
}
}
else continue;
}
}
int main()
{
int m,n;
char a;
cin>>n>>m;
cin.get();//吸收换行符
ans=true;
while(n--)
{
getline(cin,value);
storage();
}
while(m--)
{
string order;
cin>>order;
cout << (json[order] == "" ? "NOTEXIST" : json[order]) << endl;
}
return 0;
}
3.201709-3
由于保密工作做得很好,并不是所有部门之间都互相知道彼此的存在。只有当两个部门之间可以直接或间接传递信息时,他们才彼此知道对方的存在。部门之间不会把自己知道哪些部门告诉其他部门。
![](https://i-blog.csdnimg.cn/blog_migrate/37d55fc34cbaad3cbac2a3242dfef80f.png)
上图中给了一个4个部门的例子,图中的单向边表示通路。部门1可以将消息发送给所有部门,部门4可以接收所有部门的消息,所以部门1和部门4知道所有其他部门的存在。部门2和部门3之间没有任何方式可以发送消息,所以部门2和部门3互相不知道彼此的存在。
现在请问,有多少个部门知道所有 N个部门的存在。或者说,有多少个部门所知道的部门数量(包括自己)正好是 N。
接下来 M行,每行两个整数 a, b,表示部门 a到部门 b有一条单向通路。
1 2
1 3
2 4
3 4
对于60%的评测用例,1 ≤ N ≤ 100,1 ≤ M ≤ 1000;
对于100%的评测用例,1 ≤ N ≤ 1000,1 ≤ M ≤ 10000。
考点:DFS暴力搜图
易错:图的存储结构。
思路点拨:
首先理清题意。我们能想到的第一个做法就是找到图中能到所有点的那些点,以及找到图中能被所有点到的那些点,怎么做呢?暴力搜图。
常规的矩阵存储以及DFS遍历肯定是会超时的,所以对于图论的题目,推荐大家直接用二维的vector容器存储,既方便,又快捷。
下面说一下细节:
常规的DFS遍历中的bool visited[MAX]数组肯定是要保留的,每次访问到一个点,那么对应两个点肯定就知道了彼此,我们画图可以发现,只设置一个一维数组来计数好像会产生重复的情况。一维数组不行,我们设置一个二维数组,如果两个点知道彼此,设置这个二维数组的值为1,否则为0,这样就不会重复了。
拓展:
DFS深度优先遍历:
(1)设置bool visited[MAX]。
(2)邻接点函数求出其邻接点。
满分代码如下:
#include<bits/stdc++.h>
using namespace std;
#define MAX 1010
int c_count[MAX][MAX];
bool visited[MAX];
int N,M;
vector<int>p[MAX];
void DFS(int w,int v)
{
visited[w]=true;
c_count[w][v]=c_count[v][w]=1;
for(int i=0;i<p[w].size();i++)
{
if(!visited[p[w][i]]) DFS(p[w][i],v);
}
}
int main()
{
bool j_ans;
int result=0;
scanf("%d%d",&N,&M);
memset(c_count,0,sizeof(c_count));
while(M--)
{
int w,v;
scanf("%d%d",&w,&v);
p[w].push_back(v);
}
for(int i=1;i<=N;i++)
{
memset(visited,false,sizeof(visited));
DFS(i,i);
}
int i,j;
for(i=1; i<=N; i++)
{
for(j=1; j<=N; j++)
{
if(i==j) continue;
if(c_count[i][j]==0) break;
}
if(j>N) result++;
}
printf("%d\n",result);
return 0;
}