当前位置: 首页 > news >正文

机试准备第17天

今天进入图论的学习。图论只考察初试学过的算法,一般都是模版题。常见考点有图相关的数据结构——邻接表法,图的遍历 BFS DFS 并查集,单源最短路径迪杰斯特拉。图由顶点和边构成,度用来说明该顶点邻接边的数量情况。权值说明了边的长度情况。邻接表法使用动态数组模拟链表。下面是最简单的一个邻接表。

#include <stdio.h>
#include <vector>
using namespace std;
// A-B A-C A-D C-D
int main(){
    //A-0 B-1 C-2 D-3
    vector<int> graph[4];
    int u, v;
    u=0, v=1;
    graph[u].push_back(v);
    graph[v].push_back(u);
    
    u=0, v=2;
    graph[u].push_back(v);
    graph[v].push_back(u);
    
    u=0, v=3;
    graph[u].push_back(v);
    graph[v].push_back(u);
    
    u=2, v=3;
    graph[u].push_back(v);
    graph[v].push_back(u);
    
}

对于不相交的集合,做find,根据元素找到所在的集合,Union对多个集合进行合并。用树管理每一个集合。Find(x,y),x找到祖先,y找到祖先判断祖先是否相同。Union将一棵树加入另一棵树作为子树。树的构建容易变成一个链表,采取压缩路径的方法,下面是一个简单的并查集。

#include <stdio.h>
#include <vector>
using namespace std;
int father[1000];
//i 数组下标是集合数据编号 father[i]保存集合数据父亲的编号
//根i father[i] = i
void InitDisjointset(int n){
    //0~n-1
    for(int i = 0; i<n;i++){
        father[i] = i;
    }
}
int FindDisjointSet(int u){
    if(u == father[u]) return u;
    else {
       father[u] = FindDisjointSet(father[u]);
       return father[u];//路径压缩
    }
}
void UnionDisjointSet(int u, int v){
    int uroot = FindDisjointSet(u);
    int vroot = FindDisjointSet(v);
    father[vroot] = uroot;
}
int main(){
    //1 2 3 4 
    //5 6 7
    //0 8 
    int n = 9;
    InitDisjointset(n);
    UnionDisjointSet(1, 2);
    UnionDisjointSet(1, 3);
    UnionDisjointSet(1, 4);
    UnionDisjointSet(5, 6);
    UnionDisjointSet(5, 7);
    UnionDisjointSet(0, 8);
    for(int i = 0; i < 8;i++){
        printf("%d", father[i]);
    }
}

第一题是畅通工程。拿下。

#include <stdio.h>
#include <vector>
using namespace std;
int father[1000];
int Find(int u) {
    if (u == father[u]) return u;
    else {
        father[u] = Find(father[u]);
        return father[u];
    }
}
void Union(int u, int v) { //下标大的合并到下标小的
    int uroot = Find(u);
    int vroot = Find(v);
    father[vroot] = uroot;
}
int main() {
    int N, M;
    while (scanf("%d%d", &N, &M) != EOF) {
        //城镇从1到N M条路
        if (N == 0) break;
        for (int i = 1; i <= N; i++) {
            father[i] = i;
        }//初试化并查集
        for (int i = 0; i < M; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            if (u < v) {
                Union(u, v);
            } else Union(v, u);
        }
        int res = 0;
        for (int i = 1; i <= (N); i++) {
            if (father[i] == i) res++;
        }
        printf("%d\n", res - 1);
    }
}

第二题是这是一棵树吗。首先树不存在入度大于2的节点,已连通的uv不应当有新边在加入,同时输入完成后应当是一个连通图,边数 = 顶点数减1。挺复杂的一道题。

#include <stdio.h>
#include <vector>
using namespace std;
int father[10001];
int Find(int u){
    if(u == father[u]) return u;
    else {
        father[u] = Find(father[u]);
        return father[u];
    }
}
void Union(int u, int v){
    int uroot = Find(u);
    int vroot = Find(v);
    father[vroot] = uroot;
}
void Initset(int n ){
    for(int i = 0; i<n;i++){
        father[i] = i;
    }
}
int main(){
    int u, v;
    Initset(10001);
    int casenum = 1;
    int edgeCount = 0;
    int vertexCount = 0;
    vector<int> vertex(10001);//vertex[i] = 0说明i没出现过
    vector<int> indegree(10001);//记录i号节点入度
    bool istree = true;
    while(1){
        scanf("%d%d", &u, &v);
        if(u == -1&& v==-1) break;
        else if(u == 0&& v == 0){
            //一个图已经记录好了
            if(vertexCount != edgeCount+1){
                istree = false;//有特例,空树
            }
            if(vertexCount == 0&&edgeCount == 0) istree = true;
            if(istree == true){
                printf("Case %d is a tree.\n", casenum);
            }
            else printf("Case %d is not a tree.\n", casenum);
            //重置
            casenum++;
            Initset(10001);
            edgeCount = 0;
            vertexCount = 0;
            for(int i = 0; i<10001;i++){
                vertex[i] = 0;
                indegree[i] = 0;
            }
            istree = true;
        }
        else{
            //往当前图里加入新边
            edgeCount++;
            if(vertex[u] == 0){
                vertex[u] = 1;
                vertexCount++;
            }
            if(vertex[v] == 0){
                vertex[v] = 1;
                vertexCount++;
            }
            if(Find(u) == Find(v)){//判断是否成环
                istree = false;
            }
            else Union(u, v);
            indegree[v]++;
            if(indegree[v]>1) istree=false;
        }
    }
}

第三题是连通图​​​​​​​。

#include <stdio.h>
using namespace std;
int father[1001];
int find(int u) {
    if (u == father[u]) return u;
    else {
        father[u] = find(father[u]);
        return father[u];
    }
}
void Union(int u, int v) {
    int uroot = find(u);
    int vroot = find(v);
    father[vroot] = uroot;
}
int main() {
    int n, m;
    while (scanf("%d%d", &n, &m) != EOF) {
        if (n == 0) break;
        for (int i = 1; i <= n; i++) {
            father[i] = i;
        }
        for (int i = 0; i < m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            Union(u, v);
        }
        int res = 0;
        for (int i = 1; i <= n; i++) {
            if (i == father[i]) res++;
        }
        if (res > 1) printf("NO\n");
        else  printf("YES\n");
    }
}

第四题是第一题。好奇怪的名字。第一次没有使用大树合并小数,导致产生了段错误。

#include <stdio.h>
using namespace std;
int father[1000];
int visited[1000];
int find(int u){
    if(u == father[u]) return u;
    else{
        father[u] = find(father[u]);
        return father[u];
    }
}
void Union(int u, int v){
    int uroot = find(u);
    int vroot = find(v);
    father[vroot] = uroot;
}
int main(){
    for(int i = 0;i<1000;i++){
        father[i] = i;
        visited[i] = 0;
    }
    int u,v;
    while(scanf("%d%d", &u, &v)!=EOF){
        Union(u, v);
        visited[u] = 1;
        visited[v] = 1;
    }
    int res = 0;
    for(int i =0;i<1000;i++){
        if(i == father[i]&&visited[i]==1) res++;
    }
    printf("%d", res);
}

结果发现单纯数组开小了,难绷。

#include <stdio.h>
using namespace std;
const int maxN = 1e6;
int father[maxN];
int visited[maxN];
int find(int u){
    if(u == father[u]) return u;
    else{
        father[u] = find(father[u]);
        return father[u];
    }
}
void Union(int u, int v){
    int uroot = find(u);
    int vroot = find(v);
    father[vroot] = uroot;
}
int main(){
    for(int i = 0;i<maxN;i++){
        father[i] = i;
        visited[i] = 0;
    }
    int u,v;
    while(scanf("%d%d", &u, &v)!=EOF){
        Union(u, v);
        visited[u] = 1;
        visited[v] = 1;
    }
    int res = 0;
    for(int i =0;i<maxN;i++){
        if(i == father[i]&&visited[i]==1) res++;
    }
    printf("%d", res);
}

第五题是找出直系亲属。不太会写。拼尽全力无法战胜。

 #include <iostream>
    #include <cstdio>
    
    using namespace std;
    
    const int MAXN = 30;
    
    int children[MAXN];
    
    int Generation(int x, int y) {
        int level;
    
        level = 0;
        int a = x;
        while (children[a] != a) {
            a = children[a];
            level++;
            if (a == y) {
                return level;
            }
        }
    
        level = 0;
        int b = y;
        while (children[b] != b) {
            b = children[b];
            level--;
            if (b == x) {
                return level;
            }
        }
    
        return 0;
    }
    
    string Relationship(int level) {
        string answer;
        if (level == 0) {
            answer = "-";
        } else if (level == 1) {
            answer = "parent";
        } else if (level == 2) {
            answer = "grandparent";
        } else if (level > 2) {
            for (int j = 0; j < level - 2; ++j) {
                answer += "great-";
            }
            answer += "grandparent";
        } else if (level == -1) {
            answer = "child";
        } else if (level == -2) {
            answer = "grandchild";
        } else if (level < -2) {
            for (int j = 0; j < -2 - level; ++j) {
                answer += "great-";
            }
            answer += "grandchild";
        }
        return answer;
    }
    
    int main() {
        int n, m;
        while (scanf("%d%d", &n, &m) != EOF) {
            for (int i = 0; i < MAXN; ++i) {
                children[i] = i;
            }
            for (int i = 0; i < n; ++i) {
                char child, father, mother;
                cin >> child >> father >> mother;
                if (father - 'A' != '-') {
                    children[father - 'A'] = child - 'A';
                }
                if (mother - 'A' != '-') {
                    children[mother - 'A'] = child - 'A';
                }
            }
            for (int i = 0; i < m; ++i) {
                char guy1, guy2;
                cin >> guy1 >> guy2;
                int level = Generation(guy1 - 'A', guy2 - 'A');
                cout << Relationship(level) << endl;
            }
        }
        return 0;
    }

相关文章:

  • Suno的对手Luno:AI音乐开发「上传参考音频 - 方式一:通过二进制流的方式」 —— 「Luno Api系列|AI音乐API」第11篇
  • 【NLP 38、实践 ⑩ NER 命名实体识别任务 Bert 实现】
  • Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现与实战指南
  • Springboot中的 Mapper 无法找到的 可能原因及解决方案
  • 一个简单的井字棋(Tic-Tac-Toe)游戏的C语言实现
  • 程序化广告行业(20/89):交易模式深度剖析与价值解读
  • 基于51单片机的多功能时钟闹钟proteus仿真
  • 前端内存优化实战指南:从内存泄漏到性能巅峰
  • IMX6ULL_Pro开发板的串口应用程序实例(利用TTY子系统去使用串口)
  • 蓝桥杯[阶段总结] 二分,前缀和
  • C语言动态内存管理(上)
  • Compose 实践与探索十二 —— 附带效应
  • Webpack 基础
  • SLC跨头协作机制
  • 解析 Bootloader:嵌入式系统中不可或缺的启动程序
  • 蓝桥杯备考---- 图的存储与遍历
  • Matlab 基于SVPWM的VF三电平逆变器异步电机速度控制
  • 【Agent】OpenManus-Agent架构详细分析
  • 0-1背包问题 之 分割等和子集以及变形问题
  • 嵌入式SDIO 总线面试题及参考答案
  • 会计江湖|年报披露关注什么:独董给出的“信号”
  • 经济日报刊文:品牌经营不能让情怀唱“独角戏”
  • 碧桂园境外债务重组:相当于现有公众票据本金额逾50%的持有人已加入协议
  • 马上评丨行人转身相撞案:走路该保持“安全距离”吗
  • 2024年上市公司合计实现营业收入71.98万亿元
  • 外卖员投资失败负疚离家流浪,经民警劝回后泣不成声给父母下跪