过几天就要参加这个认证了,上周看了上把的题,虽然好久没写代码了但是应该比上次打的时候强点,加油。
地铁修建
201703-4
问题描述
试题编号: 201703-4
试题名称: 地铁修建
时间限制: 1.0s
内存限制: 256.0MB
问题描述:
问题描述
A市有n个交通枢纽,其中1号和n号非常重要,为了加强运输能力,A市决定在1号到n号枢纽间修建一条地铁。
地铁由很多段隧道组成,每段隧道连接两个交通枢纽。经过勘探,有m段隧道作为候选,两个交通枢纽之间最多只有一条候选的隧道,没有隧道两端连接着同一个交通枢纽。
现在有n家隧道施工的公司,每段候选的隧道只能由一个公司施工,每家公司施工需要的天数一致。而每家公司最多只能修建一条候选隧道。所有公司同时开始施工。
作为项目负责人,你获得了候选隧道的信息,现在你可以按自己的想法选择一部分隧道进行施工,请问修建整条地铁最少需要多少天。
输入格式
输入的第一行包含两个整数n, m,用一个空格分隔,分别表示交通枢纽的数量和候选隧道的数量。
第2行到第m+1行,每行包含三个整数a, b, c,表示枢纽a和枢纽b之间可以修建一条隧道,需要的时间为c天。
输出格式
输出一个整数,修建整条地铁线路最少需要的天数。
样例输入
6 6
1 2 4
2 3 4
3 6 7
1 4 2
4 5 5
5 6 6
样例输出
6
样例说明
可以修建的线路有两种。
第一种经过的枢纽依次为1, 2, 3, 6,所需要的时间分别是4, 4, 7,则整条地铁线需要7天修完;
第二种经过的枢纽依次为1, 4, 5, 6,所需要的时间分别是2, 5, 6,则整条地铁线需要6天修完。
第二种方案所用的天数更少。
评测用例规模与约定
对于20%的评测用例,1 ≤ n ≤ 10,1 ≤ m ≤ 20;
对于40%的评测用例,1 ≤ n ≤ 100,1 ≤ m ≤ 1000;
对于60%的评测用例,1 ≤ n ≤ 1000,1 ≤ m ≤ 10000,1 ≤ c ≤ 1000;
对于80%的评测用例,1 ≤ n ≤ 10000,1 ≤ m ≤ 100000;
对于100%的评测用例,1 ≤ n ≤ 100000,1 ≤ m ≤ 200000,1 ≤ a, b ≤ n,1 ≤ c ≤ 1000000。
所有评测用例保证在所有候选隧道都修通时1号枢纽可以通过隧道到达其他所有枢纽。
用dij或者kru,spfa都可以。
用spfa最短路,改d数组,d数组本来的意思是 d_i 从源点到i点最小花费。改成 从原点到 d_i点最大单边。 我当时就是这里写的有点问题。所以才给了40.
用kruskal的思想,把边排序后,放m-1个单边,使之不成环(其实就是判断一下当前加入边他们俩点的爸爸是不是一个爸爸。),放好之后维护一个最大值。可以证明,1-m最大边最小的那个一定在这里。
考试匆忙,等有时间在想具体证明,毕竟次小我都忘记了。。
#include <iostream>
#include <cstring>
#include <queue>
#include <cstdio>
using namespace std;
/* 用dij写一遍?
我开始写的是d数组维护的是
距离d数组越远越好。
这是肯定不会对的。
只是过了一部分样例而已。
*/
const int maxn=2e5+4;
int head[maxn];
struct Node{
int to,next,value;
}node[maxn*2];
int len;
void init(){
memset(head,-1,sizeof(head));
len=0;
}
void add(int a,int b,int c){
node[len].to=b;
node[len].value=c;
node[len].next=head[a];
head[a]=len++;
}
int d[maxn];
int rel[maxn];
int m,n;
bool vis[maxn];
void spfa(){
queue<int>q;
for(int i=0;i<=n;i++){
d[i]=1e9;
}
memset(vis,false,sizeof(vis));
memset(rel,0,sizeof(rel));
while(!q.empty()) q.pop();
q.push(1);
vis[1]=true;
d[1]=0;
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=false;
for(int i=head[u];i!=-1;i=node[i].next){
int to=node[i].to;
int cost=node[i].value;
if(d[to]>max(cost,d[u])){
d[to]=max(cost,d[u]);
if(!vis[to]){
vis[to]=true;
q.push(to);
}
}
}
}
}
int main()
{ int a,b,c;
scanf("%d%d",&m,&n);
init();
for(int i=0;i<n;i++){
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
spfa();
printf("%d\n",d[m]);
return 0;
}
#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
/* 也可以用mst来做。
用spfa算法是可以想到的,也比较直观。
因为d数组正常情况下是存的 从目标点到当前点的总距离。
我们维护的是 最小的边的距离。
用kruskal算法的话,是直接找前 m-1个边xjb鼓捣,只要他们不成边就行。
这个是类似的,当1和m联通之后就结束。并且结果一定是。
注意1 边数不一定是m-1。只是以连通性来确定的。
如果用dfs写的话,是亏的,因为我们不能保证这一结果。
*/
const int maxn=1e5+10;
struct edge{
int from,to,value;
edge(int a,int b,int c){
from=a;
to=b;
value=c;
}
};
vector<edge>v;
bool cmp(edge a,edge b){
return a.value<b.value;
}
int fa[maxn];
int find2(int a){
if(a==fa[a]) return a;
return fa[a]=find2(fa[a]);
}
int m,n;
void solve(){
sort(v.begin(),v.end(),cmp);
int rel=-1;
for(int i=0;i<=m;i++) fa[i]=i;
/*for(int i=0;i<v.size();i++){
cout<<v[i].value<<"??"<<endl;
}*/
for(int i=0;i<v.size();i++){
int x=find2(1);
int y=find2(m);
//cout<<"xialaide"<<x<<" "<<y<<endl;
if(x==y)break;
int x2=find2(v[i].from);
int y2=find2(v[i].to);
if(x2==y2)continue;
fa[y2]=x2;
rel=max(rel,v[i].value);
}
printf("%d\n",rel);
}
int main()
{ int a,b,c;
scanf("%d%d",&m,&n);
v.clear();
for(int i=0;i<n;i++){
scanf("%d%d%d",&a,&b,&c);
v.push_back(edge(a,b,c));
}
solve();
return 0;
}
问题描述
试题编号: 201709-4
试题名称: 通信网络
时间限制: 1.0s
内存限制: 256.0MB
问题描述:
问题描述
某国的军队由N个部门组成,为了提高安全性,部门之间建立了M条通路,每条通路只能单向传递信息,即一条从部门a到部门b的通路只能由a向b传递信息。信息可以通过中转的方式进行传递,即如果a能将信息传递到b,b又能将信息传递到c,则a能将信息传递到c。一条信息可能通过多次中转最终到达目的地。
由于保密工作做得很好,并不是所有部门之间都互相知道彼此的存在。只有当两个部门之间可以直接或间接传递信息时,他们才彼此知道对方的存在。部门之间不会把自己知道哪些部门告诉其他部门。
上图中给了一个4个部门的例子,图中的单向边表示通路。部门1可以将消息发送给所有部门,部门4可以接收所有部门的消息,所以部门1和部门4知道所有其他部门的存在。部门2和部门3之间没有任何方式可以发送消息,所以部门2和部门3互相不知道彼此的存在。
现在请问,有多少个部门知道所有N个部门的存在。或者说,有多少个部门所知道的部门数量(包括自己)正好是N。
输入格式
输入的第一行包含两个整数N, M,分别表示部门的数量和单向通路的数量。所有部门从1到N标号。
接下来M行,每行两个整数a, b,表示部门a到部门b有一条单向通路。
输出格式
输出一行,包含一个整数,表示答案。
样例输入
4 4
1 2
1 3
2 4
3 4
样例输出
2
样例说明
部门1和部门4知道所有其他部门的存在。
评测用例规模与约定
对于30%的评测用例,1 ≤ N ≤ 10,1 ≤ M ≤ 20;
对于60%的评测用例,1 ≤ N ≤ 100,1 ≤ M ≤ 1000;
对于100%的评测用例,1 ≤ N ≤ 1000,1 ≤ M ≤ 10000。
这题就有点衰了,我题意有点蒙蔽,以为一个点要么是全正向接受,要么全反向接受,那么做两个图,然后找到入度为0点搜索? 一分没得。
去掉入度为0条件,得35,应该是超时,
看了别人的代码,还是不错的,搜索(n*(m+n)) 1e7,一秒还可以。附上错的代码,有时间想怎么优化把。
1 35分代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <set>
using namespace std;
/*找根,暴力搜索
*/
const int maxn=1e5;
vector<int>g[maxn];
vector<int>G[maxn];
int du[maxn];
int du2[maxn];
bool vis[maxn];
set<int>s;
int m;
int solve(int x){
queue<int>q;
q.push(x);
int num=0;
memset(vis,false,sizeof(vis));
vis[x]=true;
num++;
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<g[u].size();i++){
int to=g[u][i];
if(!vis[to]){
vis[to]=true;
num++;
q.push(to);
}
}
}
if(num==m)return 1;
return 0;
}
// 下一个处理
int solve2(int x){
queue<int>q;
q.push(x);
int num=0;
memset(vis,false,sizeof(vis));
vis[x]=true;
num++;
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<G[u].size();i++){
int to=G[u][i];
if(!vis[to]){
vis[to]=true;
num++;
q.push(to);
}
}
}
if(num==m)return 1;
return 0;
}
int main()
{ int n,a,b;
scanf("%d%d",&m,&n);
for(int i=0;i<n;i++){
scanf("%d%d",&a,&b);
g[a].push_back(b);
du[b]++;
G[b].push_back(a);
du2[a]++;
}
int ans=0;
for(int i=1;i<=m;i++){
//if(du[i]==0){
if(solve(i)==1)
ans++,s.insert(i);
//}
}
for(int i=1;i<=m;i++){
//if(du2[i]==0){
if(solve2(i)==1){
ans++,s.insert(i);
}
//}
}
printf("%d\n",(int)(s.size()));
return 0;
}
参考的别人代码(不用百度了 前几个都是这样写的,)
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <set>
using namespace std;
/*找根,暴力搜索
*/
const int maxn=1e3+2;
vector<int>g[maxn];
bool vis[maxn];
int mp[maxn][maxn];
void dfs(int a,int b){
mp[b][a]=1;
mp[a][b]=1;
for(int i=0;i<g[a].size();i++){
if(!vis[g[a][i]]){
vis[g[a][i]]=true;
dfs(g[a][i],b);
}
}
}
int main()
{ int n,m,a,b;
scanf("%d%d",&m,&n);
for(int i=0;i<n;i++){
scanf("%d%d",&a,&b);
g[a].push_back(b);
}
for(int i=1;i<=m;i++){
memset(vis,false,sizeof(vis));
dfs(i,i);
}
int sum=0;
int ans=0;
for(int i=1;i<=m;i++){
sum=0;
for(int j=1;j<=m;j++){
sum+=mp[i][j];
}
if(sum==m)ans++;
}
printf("%d\n",ans);
return 0;
}
201709-2
问题描述
试题编号: 201709-2
试题名称: 公共钥匙盒
时间限制: 1.0s
内存限制: 256.0MB
问题描述:
问题描述
有一个学校的老师共用N个教室,按照规定,所有的钥匙都必须放在公共钥匙盒里,老师不能带钥匙回家。每次老师上课前,都从公共钥匙盒里找到自己上课的教室的钥匙去开门,上完课后,再将钥匙放回到钥匙盒中。
钥匙盒一共有N个挂钩,从左到右排成一排,用来挂N个教室的钥匙。一串钥匙没有固定的悬挂位置,但钥匙上有标识,所以老师们不会弄混钥匙。
每次取钥匙的时候,老师们都会找到自己所需要的钥匙将其取走,而不会移动其他钥匙。每次还钥匙的时候,还钥匙的老师会找到最左边的空的挂钩,将钥匙挂在这个挂钩上。如果有多位老师还钥匙,则他们按钥匙编号从小到大的顺序还。如果同一时刻既有老师还钥匙又有老师取钥匙,则老师们会先将钥匙全还回去再取出。
今天开始的时候钥匙是按编号从小到大的顺序放在钥匙盒里的。有K位老师要上课,给出每位老师所需要的钥匙、开始上课的时间和上课的时长,假设下课时间就是还钥匙时间,请问最终钥匙盒里面钥匙的顺序是怎样的?
输入格式
输入的第一行包含两个整数N, K。
接下来K行,每行三个整数w, s, c,分别表示一位老师要使用的钥匙编号、开始上课的时间和上课的时长。可能有多位老师使用同一把钥匙,但是老师使用钥匙的时间不会重叠。
保证输入数据满足输入格式,你不用检查数据合法性。
输出格式
输出一行,包含N个整数,相邻整数间用一个空格分隔,依次表示每个挂钩上挂的钥匙编号。
样例输入
5 2
4 3 3
2 2 7
样例输出
1 4 3 2 5
样例说明
第一位老师从时刻3开始使用4号教室的钥匙,使用3单位时间,所以在时刻6还钥匙。第二位老师从时刻2开始使用钥匙,使用7单位时间,所以在时刻9还钥匙。
每个关键时刻后的钥匙状态如下(X表示空):
时刻2后为1X345;
时刻3后为1X3X5;
时刻6后为143X5;
时刻9后为14325。
样例输入
5 7
1 1 14
3 3 12
1 15 12
2 7 20
3 18 12
4 21 19
5 30 9
样例输出
1 2 3 5 4
评测用例规模与约定
对于30%的评测用例,1 ≤ N, K ≤ 10, 1 ≤ w ≤ N, 1 ≤ s, c ≤ 30;
对于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。
水题,然而还是调了一下,是先放在拿,不然出bug
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
/*
violence
*/
const int maxn=1e3+2;
vector<pair<int,pair<int,int> > >q;
int a[maxn];
bool cmp2(pair<int,pair<int,int> >a,pair<int,pair<int,int> >b){
return a.first<b.first;
}
int main()
{ int m,n,a1,b,c;
scanf("%d%d",&m,&n);
int tim=0;
while(n--){
scanf("%d%d%d",&a1,&b,&c);
c=b+c;
q.push_back(make_pair(a1,make_pair(b,c)));
tim=max(c,tim);
}
//cout<<tim<<endl;
sort(q.begin(),q.end(),cmp2);
for(int i=1;i<=m;i++){
a[i]=i;
}
for(int i=1;i<=tim;i++){
for(int j=0;j<q.size();j++){
if(q[j].second.second==i){
for(int x=1;x<=m;x++){
if(a[x]==0){
a[x]=q[j].first;
break;
}
}
}
}
for(int j=0;j<q.size();j++){
if(q[j].second.first==i){
for(int x=1;x<=m;x++){
if(a[x]==q[j].first){
a[x]=0;break;
}
}
}
}
/*for(int i=1;i<=m;i++){
if(i!=1){
putchar(' ');
}
printf("%d",a[i]);
}
cout<<" "<<i<<endl;
cout<<endl;*/
}
for(int i=1;i<=m;i++){
if(i!=1){
putchar(' ');
}
printf("%d",a[i]);
}
cout<<endl;
return 0;
}