王道机试指南图论

例题11.1畅通工程(浙江大学上机)

#include<cstdio>
#include<iostream>
using namespace std;
int father[1000];
void initial(int n){
    for(int i = 0; i <= n; i++){
        father[i] = i;
    }
}
int find(int x){
    if(x == father[x]) return x;
    else{
        int f = find(father[x]);
        father[x] = f;
        return f;
    }
}
void union0(int x, int y){
    x = find(x);
    y = find(y);
    if(x != y){
        father[x] = y;
    }
}
int main(){
    int n, m;
    while(cin >> n && n != 0){
        cin >> m;
        initial(n);
        int num = -1;//用来记录有多少个道路
        for(int i = 0; i < m; i++){
            int ta, tb;
            cin >> ta >> tb;
            union0(ta, tb);
        }
        for(int i = 1; i <= n; i++){
            if(find(i) == i){
                num++;
            }
        }
        cout << num << endl;
    }
    return 0;
}    

例题11.2连通图(吉林大学复试)

#include<iostream>
#include<cstdio>
using namespace std;
int father[1000];
void initial(int n){
    for(int i = 0; i < n; i++){
        father[i] = i;
    }
}
int find(int x){
    if(x == father[x]) return x;
    else{
        int f = find(father[x]);
        father[x] = f;
        return f;
    }
}
void union0(int x, int y){
    x = find(x);
    y = find(y);
    if(x != y){
        father[x] = y;
    }
}
int main(){
    int n, m;
    while(cin >> n && n != 0){
        cin >> m;
        initial(n);
        for(int i = 0; i < m; i++){
            int ta, tb;
            cin >> ta >> tb;
            union0(ta, tb);
        }
        int ans = 0;
        for(int i = 0; i < n; i++){
            if(i == find(i)) ans++;
            }
        if(ans > 1) cout << "NO" <<endl;
        else cout << "YES" << endl;
    }
    return 0;
}    

例题11.3 Is it a tree?(北京大学复试上机)

#include<iostream>
#include<cstdio>
using namespace std;
int father[10000];
int indegree[10000];
bool visit[10000];
void initial(int n){
    for(int i = 0; i < n; i++){
        father[i] = i;
        indegree[i] = 0;
        visit[i] = false;
    }
}
int findfather(int x){
    if(x == father[x]) return x;
    else{
        int f = findfather(father[x]);
        father[x] = f;
        return f;
    }
}
void union0(int x, int y){
    x = findfather(x);
    y = findfather(y);
    if(x != y) father[x] = y;
}
bool istree(){
    bool flag = true;
    int component = 0;
    int root = 0;
    for(int i = 0; i < 10000; i++){
        if(!visit[i]) continue;
        if(father[i] == i) component++;
        if(indegree[i] == 0){
            root++;
        }else if(indegree[i] > 1){
            flag = false;
        }
    }
    if(component != 1 || root != 1) flag = false;
    if(component == 0 && root == 0) flag = true;
    return flag;
}
int main(){
    int x, y;
    int casenum = 0;
    initial(10000);
    while(cin >> x >> y &&(x != -1 && y != -1)){
        if(x == 0 && y == 0){
            if(istree()) cout << "Case " << ++casenum << " is a tree." << endl;
            else cout << "Case " << ++casenum << " is not a tree." << endl;
            initial(10000);
        }
        else{
            union0(x, y);
            indegree[y]++;
            visit[x] = true;
            visit[y] = true;
        }
    }
    return 0;
}  

习题11.4找出直系亲属(浙江大学上机)

#include<cstdio>
#include<iostream>
#include<string>
#include<cctype>
using namespace std;
int son[30] = {0};
int num;
int relnum(int a, int b){//判断b是否为a的后代。如果是,返回辈分差,不是,返回0 
    int now = a;//表示当前遍历的结点 
    while(now != 0){//一直循环a到没有儿子 
        if(now == b){//表示a的后代中找到了b,返回辈分差 
            return num;
        }
        num++;
        now = son[now];
    }
    return 0;//没有儿子跳出循环,返回0 
}
void print(int a, int b){//根据辈分差进行输出 
    num = 0;
    int tnum1 = relnum(a, b);//记录ab辈分差差(a的辈分大于b) 
    num = 0;
    int tnum2 = relnum(b, a); 
    if(tnum1 == 0 && tnum2 == 0){
        cout << "-";
    }else if(tnum1 != 0 && tnum2 == 0){//b是a后代 
        for(int i = 2; i < tnum1; i++) cout << "great-";//tnum为2的时候输出一个great
        if(tnum1 == 1) cout << "parent";
        if(tnum1 > 1) cout << "grandparent";
    }else if(tnum2 != 0 && tnum1 == 0){
        for(int i = 2; i < tnum2; i++) cout << "great-";//tnum为2的时候输出一个great
        if(tnum2 == 1) cout << "child";
        if(tnum2 > 1) cout << "grandchild";
    } 
}
int main(){
    int n, m;
    while(cin >> n >> m){
        string s;
        for(int i = 0; i < n; i++){//记录每个人的儿子 
            cin >> s;
            if(isalpha(s[1])) son[s[1]-'A'+1] = s[0]-'A'+1;
            if(isalpha(s[2])) son[s[2]-'A'+1] = s[0]-'A'+1;
        }
        for(int i = 0; i < m; i++){//用于测试 
            cin >> s;
            print(s[0]-'A'+1, s[1]-'A'+1);
            cout << endl;
        }
    }
    return 0;
}  

习题11.2第一题(上海交通大学复试)

#include<iostream>
#include<cstdio>
#include<set>
using namespace std;
int father[1000000];
int ans = 0;//用来记录联通分支个数
void initial(){//用于初始化并查集数组
    for(int i = 0; i < 1000000; i++){
        father[i] = -1;
    }
}
int findfather(int x){//用于查找 
    if(father[x] == -1) return x;
    else{
        int f = findfather(father[x]);
        father[x] = f;
        return f;
    }
}
void union0(int a, int b){//用于合并 
    int fa = findfather(a);
    int fb = findfather(b);
    if(fa != fb){
        father[fa] = fb;
    }
} 
int main(){
    int i, j;
    set<int> st;//用来统计最大的数是多大
    initial();
    while(cin >> i >> j){
        st.insert(i);
        st.insert(j);
        union0(i, j);
    }
    int ans = 0;
    for(auto it = st.begin(); it != st.end(); it++){
        if(father[*it] == -1) ans++;
    }
    cout << ans << endl;
    return 0;
}  

例题11.4 还是畅通工程(浙江大学)

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 5000;
int father[100];
struct edge{//定义边
    int u, v;//连接边的两个顶点
    int cost;//边的权重
}e[MAXN];
bool cmp1(edge a, edge b){//排序的比较函数
    return a.cost < b.cost;
}
int findroot(int x){
    if(father[x] == x) return x;
    else{
        int f = findroot(father[x]);
        father[x] = f;
        return f;
    }
}
int kruskal(int n, int m){
    int ans = 0, num_edge = 0;
    for(int i = 0; i < n; i++) father[i] = i;//初始化并查集
    sort(e, e + m, cmp1);
    for(int i = 0; i < m; i++){
        int fu = findroot(e[i].u);
        int fv = findroot(e[i].v);
        if(fu != fv){
            father[fu] = fv;
            ans += e[i].cost;
            num_edge++;
        }
    }
    return ans;
}
int main(){
    int n, m, u, v, cost;//n个点,m条边
    while(cin >> n && n != 0){
        m = n * (n-1) / 2;
        for(int i = 0; i < m; i++){
            cin >> u >> v >> cost;
            e[i].u = u;
            e[i].v = v;
            e[i].cost = cost;
         }
        cout << kruskal(n, m) << endl;
    }
    return 0;
}    

例题11.5继续畅通工程(浙江大学上机)

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int father[100];
struct edge{
    int u, v;//表示边的两个端点
    int cost;//表示花费
    int build;//表示是否已经修建
}e[5000];

bool cmp1(edge a, edge b){
    return a.cost < b.cost;
}
int findroot(int x){
    if(x == father[x]) return x;
    else{
        int f = findroot(father[x]);
        father[x] = f;
        return f;
    }
}
int kruskal(int n, int m){
    sort(e, e + m, cmp1);
    for(int i = 0; i < n; i++) father[i] = i;
    int ans = 0, edge_num = 0;
    for(int i = 0; i < m; i++){
        int fu = findroot(e[i].u);
        int fv = findroot(e[i].v);
        if(fu != fv){
            father[fu] = fv;//合并
            ans += e[i].cost;
        }
    }
    return ans;
}
int main(){
    int n, m, u, v, cost, build;
    while(cin >> n && n != 0){
        m = n * (n - 1) / 2;
        for(int i = 0; i < m; i++){
            cin >> u >> v >> cost >> build;
            father[u] = v;
            e[i].u = u;
            e[i].v = v;
            if(build == 0) e[i].cost = cost;
            else e[i].cost = 0;
        }
        cout << kruskal(n, m) << endl;
    }
    return 0;
}    

习题11.4 Freckles(北京大学复试上机)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
struct edge{//定义边
    int u, v;
    double cost;
}e[5000];
struct point{//定义点
    double x, y;
}p[100];
bool cmp1(edge a, edge b){//定义比较函数
    return a.cost < b.cost;
}
int father[100];
int findroot(int x){//找根结点,并且剪枝
    if(x == father[x]) return x;
    else{
        int f = findroot(father[x]);
        father[x] = f;
        return f;
    }
}
double kruskal(int n, int m){
    sort(e, e + m, cmp1);
    double ans = 0;
    for(int i = 0; i < n; i++) father[i] = i;//初始化并查集 
    for(int i = 0; i < m; i++){
        int fu = findroot(e[i].u);
        int fv = findroot(e[i].v);
        if(fu != fv){
            father[fu] = fv;
            ans += e[i].cost;
        }
    }
    return ans;
}
double lengthcost(double a, double b, double c, double d){//计算两个点之间的距离
    double temp = (a-c)*(a-c) + (b-d)*(b-d);
    return sqrt(temp);
    cout << " te = " << endl; 
}
int main(){
    int n, m;//n个点,m个边
    cin  >> n;
    m = n *(n - 1) / 2;
    for(int i = 0; i < n; i++){//输入点
        cin >> p[i].x >> p[i].y;
    }
    int num_m = 0;//开始初始化边的个数
    for(int i = 0; i < n; i++){
        for(int j = i + 1; j < n; j++){
            father[i] = j;//i点和j点连接
            e[num_m].u = i;//定义边的两个点
            e[num_m].v = j;
            e[num_m].cost = lengthcost(p[i].x, p[i].y, p[j].x, p[j].y);//定义边的=距离
            num_m++;
        }
    }
    double t = kruskal(n, m);
    printf("%.2f\n", t);
    return 0;
}  

例题11.7最短路径问题(浙江大学上机)

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXV = 1010;
const int INF = 100000000;
int n, m, s, t;//定义n个点,m个边,源点s,目的点t
int G[MAXV][MAXV], cost[MAXV][MAXV];//无向图采用邻接矩阵
int d[MAXV], c[MAXV];//定义距离和花费
bool vis[MAXV] = {false};//标记数组
void Dijstra(int s){
    fill(d, d + MAXV, INF);
    fill(c, c + MAXV, INF);
    c[s] = 0;
    d[s] = 0;
    for(int i = 1; i <= n; i++){
        int u = -1, MIN = INF;//u为当前点,MIN为当前最小值
        for(int j = 1; j <= n; j++){//找到与u相连的距离最小的点
            if(vis[j] == false && d[j] < MIN){
                u = j;
                MIN = d[j];
            }
        }
        if(u == -1) return;//找不到与u相连的点就退出
        vis[u] = true;
        for(int v = 1; v <= n; v++){
            if(vis[v] == false && G[u][v] != INF ){
                if(d[u] + G[u][v] < d[v]){
                    d[v] = d[u] + G[u][v];
                    c[v] = c[u] + cost[u][v];
                }else if(d[u] + G[u][v] == d[v] && c[u] + cost[u][v] < c[v]){
                    c[v] = c[u] + cost[u][v];
                }
            }
        }
    }
}
int main(){
    while(cin >> n >> m){
        if(n == 0 && m == 0) break;
        fill(G[0], G[0] + MAXV * MAXV, INF);//图的初始化
        for(int i = 0; i < m; i++){//用于输入
            int ta, tb, td, tp;
            cin >> ta >> tb >> td >> tp;
            if(G[ta][tb] > td){
                G[ta][tb] = G[tb][ta] = td;//G存距离,coat存花费
                cost[ta][tb] = cost[tb][ta] = tp;
            }else if(G[ta][tb] == td && cost[ta][tb] < tp){
                cost[ta][tb] = cost[tb][ta] = tp;
            }
        }
        cin >> s >> t;
        Dijstra(s);
        cout << d[t] << " " << c[t] << endl;
    }
    return 0;
}