团体赛搜索1补题

7-1 列出连通集
给定一个有N个顶点和E条边的无向图,请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N−1编号。进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。

输入格式:
输入第1行给出2个整数N(0<N≤10)和E,分别是图的顶点数和边数。随后E行,每行给出一条边的两个端点。每行中的数字之间用1空格分隔。

输出格式:
按照"{ v
​1
​​ v
​2
​​ … v
​k
​​ }"的格式,每行输出一个连通集。先输出DFS的结果,再输出BFS的结果。

输入样例:
8 6
0 7
0 1
2 0
4 1
2 4
3 5
输出样例:
{ 0 1 4 2 7 }
{ 3 5 }
{ 6 }
{ 0 1 2 7 4 }
{ 3 5 }
{ 6 }

#include<bits/stdc++.h>
using namespace std;
const int N=50;
bool st[N];
int g[N][N];
int n,m;
void dfs(int x)
{
    st[x]=true;
    printf("%d ",x);
    for(int i=0;i<n;i++){
        if(!st[i]&&g[x][i]){
            st[i]=true;
            dfs(i);
        }
    }
}
void bfs(int x)
{
    queue<int>q;
    q.push(x);
    while(q.size()){
        auto t=q.front();
        printf("%d ",t);
        st[t]=true;
        q.pop();
        for(int i=0;i<n;i++){
            if(!st[i]&&g[t][i]){
                st[i]=true;
                q.push(i);
            }
        }
    }
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        g[a][b]=g[b][a]=1;
    }
    for(int i=0;i<n;i++){
        if(!st[i]){
            printf("{ ");
            dfs(i);
            printf("}\n");
        }
    }
    memset(st,0,sizeof(st));
    for(int i=0;i<n;i++){
        if(!st[i]){
            printf("{ ");
            bfs(i);
            printf("}\n");
        }
    }
    return 0;
}

7-3 地下迷宫探索 (30 分)
地道战是在抗日战争时期,在华北平原上抗日军民利用地道打击日本侵略者的作战方式。地道网是房连房、街连街、村连村的地下工事,如下图所示。

我们在回顾前辈们艰苦卓绝的战争生活的同时,真心钦佩他们的聪明才智。在现在和平发展的年代,对多数人来说,探索地下通道或许只是一种娱乐或者益智的游戏。本实验案例以探索地下通道迷宫作为内容。

假设有一个地下通道迷宫,它的通道都是直的,而通道所有交叉点(包括通道的端点)上都有一盏灯和一个开关。请问你如何从某个起点开始在迷宫中点亮所有的灯并回到起点?

输入格式:
输入第一行给出三个正整数,分别表示地下迷宫的节点数N(1<N≤1000,表示通道所有交叉点和端点)、边数M(≤3000,表示通道数)和探索起始节点编号S(节点从1到N编号)。随后的M行对应M条边(通道),每行给出一对正整数,分别是该条边直接连通的两个节点的编号。

输出格式:
若可以点亮所有节点的灯,则输出从S开始并以S结束的包含所有节点的序列,序列中相邻的节点一定有边(通道);否则虽然不能点亮所有节点的灯,但还是输出点亮部分灯的节点序列,最后输出0,此时表示迷宫不是连通图。

由于深度优先遍历的节点序列是不唯一的,为了使得输出具有唯一的结果,我们约定以节点小编号优先的次序访问(点灯)。在点亮所有可以点亮的灯后,以原路返回的方式回到起点。

输入样例1:
6 8 1
1 2
2 3
3 4
4 5
5 6
6 4
3 6
1 5
输出样例1:
1 2 3 4 5 6 5 4 3 2 1
输入样例2:
6 6 6
1 2
1 3
2 3
5 4
6 5
6 4
输出样例2:
6 4 5 4 6 0
注意:没全过的原因就是没考虑到点重复出现的情况,参考了一份代码。

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int g[N][N];
bool st[N];
int n,m,v0,top;
int path[N];
void dfs(int x)
{
    path[top++]=x;
    for(int i=1;i<=n;i++){
        if(g[x][i]&&!st[i]){
            st[i]=true;
            dfs(i);
            path[top++]=x;
        }
    }
}
int main()
{
    cin>>n>>m>>v0;
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        g[a][b]=g[b][a]=1;
    }
    st[v0]=true;
    dfs(v0);
    if(top!=2*n-1) path[top++]=0;
    for(int i=0;i<top;i++){
        if(i==top-1) cout<<path[i];
        else cout<<path[i]<<" ";
    }
    return 0;
}

7-6 排座位 (25 分)
布置宴席最微妙的事情,就是给前来参宴的各位宾客安排座位。无论如何,总不能把两个死对头排到同一张宴会桌旁!这个艰巨任务现在就交给你,对任何一对客人,请编写程序告诉主人他们是否能被安排同席。

输入格式:
输入第一行给出3个正整数:N(≤100),即前来参宴的宾客总人数,则这些人从1到N编号;M为已知两两宾客之间的关系数;K为查询的条数。随后M行,每行给出一对宾客之间的关系,格式为:宾客1 宾客2 关系,其中关系为1表示是朋友,-1表示是死对头。注意两个人不可能既是朋友又是敌人。最后K行,每行给出一对需要查询的宾客编号。

这里假设朋友的朋友也是朋友。但敌人的敌人并不一定就是朋友,朋友的敌人也不一定是敌人。只有单纯直接的敌对关系才是绝对不能同席的。

输出格式:
对每个查询输出一行结果:如果两位宾客之间是朋友,且没有敌对关系,则输出No problem;如果他们之间并不是朋友,但也不敌对,则输出OK;如果他们之间有敌对,然而也有共同的朋友,则输出OK but…;如果他们之间只有敌对关系,则输出No way。

输入样例:
7 8 4
5 6 1
2 7 -1
1 3 1
3 4 1
6 7 -1
1 2 1
1 4 1
2 3 -1
3 4
5 7
2 3
7 2
输出样例:
No problem
OK
OK but…
No way

#include<bits/stdc++.h>
using namespace std;
const int N=200;
int g[N][N],p[N];
int n,m,k;
int find(int x)
{
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}
int main()
{
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++) p[i]=i;
    for(int i=0;i<m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        g[a][b]=g[b][a]=c;
        if(c==1){
            if(find(a)!=find(b))
                p[find(a)]=find(b);
        }
    }
    for(int i=0;i<k;i++){
        int a,b;
        cin>>a>>b;
        int x=find(a);
        int y=find(b);
        if(g[a][b]!=-1&&x==y) printf("No problem\n");
        else if(g[a][b]==0) printf("OK\n");
        else if(x==y&&g[a][b]==-1) printf("OK but...\n");
        else if(x!=y&&g[a][b]==-1) printf("No way\n");
    }
    return 0;
}

7-7 红色警报 (25 分)
战争中保持各个城市间的连通性非常重要。本题要求你编写一个报警程序,当失去一个城市导致国家被分裂为多个无法连通的区域时,就发出红色警报。注意:若该国本来就不完全连通,是分裂的k个区域,而失去一个城市并不改变其他城市之间的连通性,则不要发出警报。

输入格式:
输入在第一行给出两个整数N(0 < N ≤ 500)和M(≤ 5000),分别为城市个数(于是默认城市从0到N-1编号)和连接两城市的通路条数。随后M行,每行给出一条通路所连接的两个城市的编号,其间以1个空格分隔。在城市信息之后给出被攻占的信息,即一个正整数K和随后的K个被攻占的城市的编号。

注意:输入保证给出的被攻占的城市编号都是合法的且无重复,但并不保证给出的通路没有重复。

输出格式:
对每个被攻占的城市,如果它会改变整个国家的连通性,则输出Red Alert: City k is lost!,其中k是该城市的编号;否则只输出City k is lost.即可。如果该国失去了最后一个城市,则增加一行输出Game Over.。

输入样例:
5 4
0 1
1 3
3 0
0 4
5
1 2 0 4 3
输出样例:
City 1 is lost.
City 2 is lost.
Red Alert: City 0 is lost!
City 4 is lost.
City 3 is lost.
Game Over.

#include<bits/stdc++.h>
using namespace std;
const int N=5000;
int g[N][N];
bool vis[N];
bool st[N];
int n,m,k;
void dfs(int x)
{
    vis[x]=true;
    for(int i=0;i<n;i++){
        if(!vis[i]&&g[x][i]){
            vis[i]=true;
            dfs(i);
        }
    }
}
int get_cnt()
{
    int cnt=0;
    memset(vis,0,sizeof(vis));
    for(int i=0;i<n;i++){
        if(!vis[i]&&!st[i]){
            dfs(i);
            cnt++;
        }
    }
    return cnt;
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        g[a][b]=g[b][a]=1;
    }
    int k;
    cin>>k;
    int cnt1=get_cnt();
    for(int i=0;i<k;i++){
        int x;
        cin>>x;
        st[x]=true;
        for(int i=0;i<n;i++)
            g[i][x]=g[x][i]=0;
        int cnt2=get_cnt();
        if(cnt2>=cnt1+1)
            printf("Red Alert: City %d is lost!\n",x);
        else
            printf("City %d is lost.\n",x);
        cnt1=cnt2;
    }
    if(k==n) printf("Game Over.");
    return 0;
}

7-11 图着色问题 (25 分)
图着色问题是一个著名的NP完全问题。给定无向图G=(V,E),问可否用K种颜色为V中的每一个顶点分配一种颜色,使得不会有两个相邻顶点具有同一种颜色?

但本题并不是要你解决这个着色问题,而是对给定的一种颜色分配,请你判断这是否是图着色问题的一个解。

输入格式:
输入在第一行给出3个整数V(0<V≤500)、E(≥0)和K(0<K≤V),分别是无向图的顶点数、边数、以及颜色数。顶点和颜色都从1到V编号。随后E行,每行给出一条边的两个端点的编号。在图的信息给出之后,给出了一个正整数N(≤20),是待检查的颜色分配方案的个数。随后N行,每行顺次给出V个顶点的颜色(第i个数字表示第i个顶点的颜色),数字间以空格分隔。题目保证给定的无向图是合法的(即不存在自回路和重边)。

输出格式:
对每种颜色分配方案,如果是图着色问题的一个解则输出Yes,否则输出No,每句占一行。

输入样例:
6 8 3
2 1
1 3
4 6
2 5
2 4
5 4
5 6
3 6
4
1 2 3 3 1 2
4 5 6 6 4 5
1 2 3 4 5 6
2 3 4 2 3 4
输出样例:
Yes
Yes
No
No
思路:建立邻接表循环临点颜色是否相同,坑点需要用set存不同点数量,如果不相等直接错。

#include<bits/stdc++.h>
using namespace std;
const int N=500000;
int h[N],e[N],ne[N],idx;
bool st[N];
int color[N];
int n,m,k;
set<int>s;
void add(int a,int b)
{
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
int judje()
{
    for(int i=1;i<=n;i++){
        for(int j=h[i];j!=-1;j=ne[j]){
           if(color[i]==color[e[j]])
                return 0;
        }
    }
    return 1;
}
int main()
{
    memset(h,-1,sizeof(h));
    cin>>n>>m>>k;
    for(int i=1;i<=m;i++){
        int a,b;
        cin>>a>>b;
        add(a,b);
        add(b,a);
    }
    int Q;
    cin>>Q;
    while(Q--){
        s.clear();
        for(int i=1;i<=n;i++){
            cin>>color[i];
            s.insert(color[i]);
        }
        if(s.size()!=k) cout<<"No"<<endl;
        else{
            int t=judje();
            if(t==1) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值