ccf五年试题三年模拟

过几天就要参加这个认证了,上周看了上把的题,虽然好久没写代码了但是应该比上次打的时候强点,加油。
地铁修建
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值