牛客网 2018年全国多校算法寒假训练营练习比赛(第四场) 题解

A-石油采集

题目描述
随着海上运输石油泄漏的问题,一个新的有利可图的行业正在诞生,那就是撇油行业。如今,在墨西哥湾漂浮的大量石油,吸引了许多商人的目光。这些商人们有一种特殊的飞机,可以一瓢略过整个海面20米乘10米这么大的长方形。(上下相邻或者左右相邻的格子,不能斜着来)当然,这要求一瓢撇过去的全部是油,如果一瓢里面有油有水的话,那就毫无意义了,资源完全无法利用。现在,商人想要知道,在这片区域中,他可以最多得到多少瓢油。
地图是一个N×N的网络,每个格子表示10m×10m的正方形区域,每个区域都被标示上了是油还是水
链接:https://www.nowcoder.net/acm/contest/76/A
题解
遍历所有点,判断当前点能不能和周围某个点相消。例如

样例一
###
#..
###
样例二
###
#..
#..
#..

判断某点能否相消,就要看与其周围直线连接的点是否是#。
可以证明的是,永远是从最开头消点,例如样例二。最好的方案是(0,0)(1,0)、(0,1)(0,2)、(2,0)(3,0)。当我们判断(0,0)能否与其周围点相消时,先要dfs走到这个方向的开头,反过来消。

代码

#include 
using namespace std;
int n,ans;
char mp[55][55];
const int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
bool dfs(int x,int y)
{
    for(int i=0;i<4;i++){
        int dx=x+dir[i][0];
        int dy=y+dir[i][1];
        if(dx=0&&dy=0&&mp[dx][dy]=='#'){
            mp[x][y]='.';
            if(!dfs(dx,dy)){
                mp[dx][dy]='.';
                ans++;
                return true;
            }
            mp[x][y]='#';
        }
    }
    return false;
}
int main()
{
    int T;
    while(~scanf("%d",&T)){
    for(int cas=1;cas<=T;cas++){
        ans=0;
        scanf("%d",&n);
        for(int i=0;iscanf("%s",mp[i]);
        for(int i=0;ifor(int j=0;jif(mp[i][j]=='#'){dfs(i,j);}
            }
        }
        printf("Case %d: %d\n",cas,ans);
    }}
}

B-道路建设

题目描述
随着如今社会的不断变化,交通问题也变得越来越重要,所以市长决定建设一些公路来方便各个城市之间的贸易和交易。虽然市长的想法很好,但是他也遇到了一般人也经常头疼的问题,那就是手头的经费有限……在规划过程中,设计师们已经预算出部分城市之间建设公路的经费需求。现在市长想知道,它能不能将他的m个城市在有限的经费内实现公路交通。如果可以的话,输出Yes,否则输出No(两个城市不一定要直接的公路相连,间接公路到达也可以。)
链接:https://www.nowcoder.net/acm/contest/76/B
备注:两个城市之间可能存在多条线路
题解
最小生成树。这里我用的Kruskal算法。
代码

#include 
using namespace std;
struct node
{
    int v1,v2;
    int h;
    bool operator<(const node&other)const{
        return h10005];
int main()
{
    int c,n,m;
    while(~scanf("%d%d%d",&c,&n,&m)){
        int ans=0;
        for(int i=0;i//输入两点及两点路径。
            scanf("%d%d%d",&nod[i].v1,&nod[i].v2,&nod[i].h);
        }
        sort(nod,nod+n);//给两点间的路径排序。优先选择最小。
        bool vis[105]={0};
        vis[nod[0].v1]=1;//默认先选择nod[0].x为起始点。
        int NN=1;//已连通的点数
        for(int i=0;iint num=vis[nod[i].v1]+vis[nod[i].v2];
            if(NN==m) break;//若所有点都连通则退出。
            if(num==1){
                ans+=nod[i].h;
                NN++;
                vis[nod[i].v1]=vis[nod[i].v2]=1;
                i=0;//重新遍历未连通的最小路径的点。
            }
        }
        printf("%s\n",ans<=c?"Yes":"No");
    }
    return 0;
}

C-求交集

题目描述
给你两个升序排列的集合,求出两个集合的交集。
链接:https://www.nowcoder.net/acm/contest/76/C
题解
先把集合A用数组V存起来。再用二分搜索确定集合B中的某个数是否在集合A中,若在即输出。
代码

#include 
using namespace std;
vector<int>v;
const int inf=1000000001;
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        for(int i=0;iint t;scanf("%d",&t);
            v.push_back(t);
        }
        int tem=inf;
        for(int i=0;iint t;scanf("%d",&t);
            if(binary_search(v.begin(),v.end(),t)==true){
                if(tem!=inf) printf("%d ",tem);
                tem=t;
            }
        }
        if(tem!=inf) printf("%d\n",tem);
        else printf("empty\n");
    }
    return 0;
}

E-通知小弟

题目描述
在战争时期,A国派出了许多间谍到其他国家去收集情报。因为间谍需要隐秘自己的身份,所以他们之间只是单向联系。所以,某个间谍只能单向联系到一部分的间谍。同时,间谍也不知道跟他联系的是谁。
HA是间谍们的老大,但他也只能联系到部分的间谍。HA现在有一项命令有告诉所有的间谍。HA想要知道他至少要告诉多少个他能联系上的间谍才能通知到所有的间谍。
链接:https://www.nowcoder.net/acm/contest/76/E
题解
题目意思是求你最少需要联系几个你能联系的小弟。那么就容易出现考虑不到位的情况。比如HA能联系间谍A和B,B也能联系A,这种情况只要联系B即可。具体看我另外给出的测试样例。
样例一

3 3
1 2 3
0
2 1 3
0

样例二

4 2
1 2
0
1 3
1 4
1 2

代码

#include 
using namespace std;
vector<int>x;
vector<int>a[505];
bool vis[505],cst[505];
int ans=0;
void dfs(int k,int s)
{
    vis[k]=1;
    for(int i=0;iint v=a[k][i];
        if(!vis[v]) dfs(v,s);
        else if(v!=s &&cst[v]){
            cst[v]=false;
            ans--;
        }
    }
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++) a[i].clear();
        x.clear();
        ans=0;
        for(int i=1;i<=m;i++){
            int xx;scanf("%d",&xx);
            x.push_back(xx);
        }
        for(int i=1;i<=n;i++){
            int nn;scanf("%d",&nn);
            for(int j=1;j<=nn;j++){
                int aa;scanf("%d",&aa);
                a[i].push_back(aa);
            }
        }
        for(int i=0;iif(!vis[x[i]]){
                ans++;
                cst[x[i]]=true;
                dfs(x[i],x[i]);
            }
        }
        int flag=1;
        for(int i=1;i<=n;i++){
            if(!vis[i]){flag=0;break;}
        }
        printf("%d\n",flag?ans:-1);
    }
    return 0;
}

F-Call to your teacher

题目描述
从实验室出来后,你忽然发现你居然把自己的电脑落在了实验室里,但是实验室的老师已经把大门锁上了。更糟的是,你没有那个老师的电话号码。你开始给你知道的所有人打电话,询问他们有没有老师的电话,如果没有,他们也会问自己的同学来询问电话号码。那么,你能联系到老师并且拿到电脑吗。
链接:https://www.nowcoder.net/acm/contest/76/F
题解
dfs搜索即可。注意设置vis[]数组,免去多余的操作。否则在OJ上提示内存超限。
代码

#include 
using namespace std;
vector<vector<int> > son;
int n,m;
int flag=0;
bool vis[55];
void dfs(int k)
{
    vis[k]=1;
    if(k==n){flag=1;return;}
    for(int i=0;iif(flag==1) return;
        if(vis[son[k][i]]==0) dfs(son[k][i]);
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        flag=0;
        while(!son.empty()) son.clear();
        memset(vis,0,sizeof(vis));
        son.resize(n+1);
        for(int i=1;i<=m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            son[x].push_back(y);
        }
        dfs(1);
        printf("%s\n",(flag==1?"Yes":"No"));
    }
}

H-老子的全排列呢

题目描述
老李见和尚赢了自己的酒,但是自己还舍不得,所以就耍起了赖皮,对和尚说,光武不行,再来点文的,你给我说出来1-8的全排序,我就让你喝,这次绝不耍你,你能帮帮和尚么?
链接:https://www.nowcoder.net/acm/contest/76/H
题解
主要用到了一个函数next_permutation。next_permutation函数详析<<
或者用全排列的方法。子集生成样题<<
代码

#include 
using namespace std;
int main()
{
    int a[10]={1,2,3,4,5,6,7,8};
    do{
            for(int i=0;i<8;i++){
                cout<if(i==7) cout<else cout<<" ";
            }
    }while(next_permutation(a,a+8));
    return 0;
}

你可能感兴趣的:(C/C++,ACM套题)