最小生成树,POJ和HDU几道题目的解题报告(基于自己的模板)

首先POJ题目:

链接:1251 Jungle Roads

题目大意:纯求最小生成树,结果为最小权值边的和。采用邻接表

#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;

#define maxn 30  //最大顶点个数
int n;       //顶点数,边数

struct arcnode  //边结点
{
    int vertex;     //与表头结点相邻的顶点编号
    int weight;     //连接两顶点的边的权值
    arcnode * next; //指向下一相邻接点
    arcnode() {}
    arcnode(int v,int w):vertex(v),weight(w),next(NULL) {}
};

struct vernode      //顶点结点,为每一条邻接表的表头结点
{
    int vex;    //当前定点编号
    arcnode * firarc;   //与该顶点相连的第一个顶点组成的边
}Ver[maxn];

void Init()  //建立图的邻接表需要先初始化,建立顶点结点
{
    for(int i = 1; i <= n; i++)
    {
        Ver[i].vex = i;
        Ver[i].firarc = NULL;
    }
}

void Insert(int a, int b, int w)   //头插法,效率更高,但不能去重边
{
    arcnode * q = new arcnode(b, w);
    if(Ver[a].firarc == NULL)
        Ver[a].firarc = q;
    else
    {
        arcnode * p = Ver[a].firarc;
        q->next = p;
        Ver[a].firarc = q;
    }
}
struct node     //保存key值的结点
{
    int v;
    int key;
    friend bool operator<(node a, node b)   //自定义优先级,key小的优先
    {
        return a.key > b.key;
    }
};

#define INF 0xfffff    //权值上限
int parent[maxn];   //每个结点的父节点
bool visited[maxn]; //是否已经加入树种
node vx[maxn];      //保存每个结点与其父节点连接边的权值
priority_queue<node> q; //优先队列stl实现
void Prim(int s)    //s表示根结点
{
    for(int i = 1; i <= n; i++) //初始化
    {
        vx[i].v = i;
        vx[i].key = INF;
        parent[i] = -1;
        visited[i] = false;
    }
    vx[s].key = 0;
    q.push(vx[s]);
    while(!q.empty())
    {
        node nd = q.top();  //取队首,记得赶紧pop掉
        visited[nd.v] = true;
        q.pop();
        arcnode * p = Ver[nd.v].firarc;
        while(p != NULL)    //找到所有相邻结点,若未访问,则入队列
        {
            if(!visited[p->vertex] && p->weight < vx[p->vertex].key)
            {
                parent[p->vertex] = nd.v;
                vx[p->vertex].key = p->weight;
                vx[p->vertex].v = p->vertex;
                q.push(vx[p->vertex]);
            }
            p = p->next;
        }
    }
}
void Show()     //打印图的邻接表(有权值)
{
    for(int i = 1; i <= n; i++)
    {
        cout << Ver[i].vex;
        arcnode * p = Ver[i].firarc;
        while(p != NULL)
        {
            cout << "->(" << p->vertex << "," << p->weight << ")";
            p = p->next;
        }
        cout << "->NULL" << endl;
    }
}
int main()
{
    int k, d;
    char x, y;
    while(cin >> n && n!=0)
    {
        Init();
        for(int i = 1; i < n; i++)
        {
            cin >> x >> k;
            while(k--)
            {
                cin >> y >> d;
                Insert(x-'A'+1, y-'A'+1, d);
                Insert(y-'A'+1, x-'A'+1, d);
            }
        }
        Prim(1);
        int ans = 0;
        for(int i = 1; i <= n; i++)
            ans += vx[i].key;
        printf("%d\n", ans);
    }
    return 0;
}

链接:1258 Agri-Net

题目大意:求最小生成树,邻接矩阵实现

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;

#define maxn 110
#define INF 100020    //预定于的最大值
int n;   //顶点数、边数
int g[maxn][maxn];      //邻接矩阵表示

struct node     //保存key值的结点
{
    int v;
    int key;
    friend bool operator<(node a, node b)   //自定义优先级,key小的优先
    {
        return a.key > b.key;
    }
};
int parent[maxn];   //每个结点的父节点
bool visited[maxn]; //是否已经加入树种
node vx[maxn];      //保存每个结点与其父节点连接边的权值
priority_queue<node> q; //优先队列stl实现
void Prim(int s)    //s表示根结点
{
    for(int i = 1; i <= n; i++) //初始化
    {
        vx[i].v = i;
        vx[i].key = INF;
        parent[i] = -1;
        visited[i] = false;
    }
    vx[s].key = 0;
    q.push(vx[s]);
    while(!q.empty())
    {
        node nd = q.top();  //取队首,记得赶紧pop掉
        q.pop();
        if(visited[nd.v] == true)   //深意,因为push机器的可能是重复但是权值不同的点,我们只取最小的
            continue;
        int st = nd.v;
        //cout << nd.v << " " << nd.key << endl;
        visited[nd.v] = true;
        for(int j = 1;  j <= n; j++)
        {
            if(j!=st && !visited[j] && g[st][j] < vx[j].key)    //判断
            {
                parent[j] = st;
                vx[j].key = g[st][j];
                q.push(vx[j]);

            }
        }
    }
}
int main()
{
    while(~scanf("%d", &n))
    {
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
            {
                scanf("%d", &g[i][j]);
                if(g[i][j] == 0)
                    g[i][j] = INF;
            }
        Prim(1);
        int ans = 0;
        for(int i = 1; i <= n; i++)
            ans += vx[i].key;
        printf("%d\n", ans);

    }
    return 0;
}

链接:1789 Truck History

题目大意:n种卡车,互相之间的差别为同一位置上不同字母的个数,这相当于每个点的权值,根据这个权值来求最小生成树。

代码,采用邻接矩阵:

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;

#define maxn 2020
#define INF 10    //预定于的最大值
int n;   //顶点数、边数
int g[maxn][maxn];      //邻接矩阵表示

struct node     //保存key值的结点
{
    int v;
    int key;
    friend bool operator<(node a, node b)   //自定义优先级,key小的优先
    {
        return a.key > b.key;
    }
};
bool visited[maxn]; //是否已经加入树种
node vx[maxn];      //保存每个结点与其父节点连接边的权值
priority_queue<node> q; //优先队列stl实现
void Prim(int s)    //s表示根结点
{
    for(int i = 1; i <= n; i++) //初始化
    {
        vx[i].v = i;
        vx[i].key = INF;
        visited[i] = false;
    }
    vx[s].key = 0;
    q.push(vx[s]);
    while(!q.empty())
    {
        node nd = q.top();  //取队首,记得赶紧pop掉
        q.pop();
        if(visited[nd.v] == true)   //深意,因为push机器的可能是重复但是权值不同的点,我们只取最小的
            continue;
        int st = nd.v;
        visited[nd.v] = true;
        for(int j = 1;  j <= n; j++)
        {
            if(j!=st && !visited[j] && g[st][j] < vx[j].key)    //判断
            {
                vx[j].key = g[st][j];
                q.push(vx[j]);
            }
        }
    }
}
char tk[2020][8];
int main()
{
    while(scanf("%d", &n), n)
    {
        for(int i = 1; i <= n; i++)
            scanf("%s", tk[i]);
        for(int i = 1; i < n; i++)
        {
            for(int j = i+1; j <= n; j++)
            {
                int cnt = 0;
                for(int k = 0; k < 7; k++)
                    if(tk[i][k] != tk[j][k])
                        cnt++;
                g[i][j] = g[j][i] = cnt;
            }
        }
        Prim(1);
        int ans = 0;
        for(int i = 1; i <= n; i++)
            ans += vx[i].key;
        printf("The highest possible quality is 1/%d.\n", ans);
    }
    return 0;
}

链接:2485 Highways

题目大意:依然是最小生成树,输出权值最大的边。

代码:

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;

#define maxn 510
#define INF 0xffffff    //预定于的最大值
int n;   //顶点数、边数
int g[maxn][maxn];      //邻接矩阵表示

struct node     //保存key值的结点
{
    int v;
    int key;
    friend bool operator<(node a, node b)   //自定义优先级,key小的优先
    {
        return a.key > b.key;
    }
};
bool visited[maxn]; //是否已经加入树中
node vx[maxn];      //保存每个结点与其父节点连接边的权值
priority_queue<node> q; //优先队列stl实现
void Prim(int s)    //s表示根结点
{
    for(int i = 1; i <= n; i++) //初始化
    {
        vx[i].v = i;
        vx[i].key = INF;
        visited[i] = false;
    }
    vx[s].key = 0;
    q.push(vx[s]);
    while(!q.empty())
    {
        node nd = q.top();  //取队首,记得赶紧pop掉
        q.pop();
        if(visited[nd.v] == true)   //深意,因为push机器的可能是重复但是权值不同的点,我们只取最小的
            continue;
        int st = nd.v;
        visited[nd.v] = true;
        for(int j = 1;  j <= n; j++)
        {
            if(j!=st && !visited[j] && g[st][j] < vx[j].key)    //判断
            {
                vx[j].key = g[st][j];
                q.push(vx[j]);
            }
        }
    }
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
                scanf("%d", &g[i][j]);
            g[i][i] = INF;
        }
        Prim(1);
        int ans = 0;
        for(int i = 1; i <= n; i++)
            if(vx[i].key > ans)
                ans = vx[i].key;
        printf("%d\n", ans);

    }
    return 0;
}

HDOJ

链接:1863 畅通工程

题目大意:求全省畅通最低成本,最小生成树,判断能否生成一棵树,如果能输出最小代价,不能输出 ? 

代码,采用邻接表:

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;

#define maxn 110  //最大顶点个数
int n, m;       //顶点数,边数

struct arcnode  //边结点
{
    int vertex;     //与表头结点相邻的顶点编号
    int weight;     //连接两顶点的边的权值
    arcnode * next; //指向下一相邻接点
    arcnode() {}
    arcnode(int v,int w):vertex(v),weight(w),next(NULL) {}
};

struct vernode      //顶点结点,为每一条邻接表的表头结点
{
    int vex;    //当前定点编号
    arcnode * firarc;   //与该顶点相连的第一个顶点组成的边
}Ver[maxn];

void Init()  //建立图的邻接表需要先初始化,建立顶点结点
{
    for(int i = 1; i <= n; i++)
    {
        Ver[i].vex = i;
        Ver[i].firarc = NULL;
    }
}

void Insert(int a, int b, int w)   //头插法,效率更高,但不能去重边
{
    arcnode * q = new arcnode(b, w);
    if(Ver[a].firarc == NULL)
        Ver[a].firarc = q;
    else
    {
        arcnode * p = Ver[a].firarc;
        q->next = p;
        Ver[a].firarc = q;
    }
}

struct node     //保存key值的结点
{
    int v;
    int key;
    friend bool operator<(node a, node b)   //自定义优先级,key小的优先
    {
        return a.key > b.key;
    }
};

#define INF 999999999    //权值上限
int parent[maxn];   //每个结点的父节点
bool visited[maxn]; //是否已经加入树种
node vx[maxn];      //保存每个结点与其父节点连接边的权值
priority_queue<node> q; //优先队列stl实现
void Prim(int s)    //s表示根结点
{
    for(int i = 1; i <= n; i++) //初始化
    {
        vx[i].v = i;
        vx[i].key = INF;
        parent[i] = -1;
        visited[i] = false;
    }
    vx[s].key = 0;
    q.push(vx[s]);
    while(!q.empty())
    {
        node nd = q.top();  //取队首,记得赶紧pop掉
        q.pop();
        if(visited[nd.v] == true)
            continue;
        visited[nd.v] = true;
        arcnode * p = Ver[nd.v].firarc;
        while(p != NULL)    //找到所有相邻结点,若未访问,则入队列
        {
            if(!visited[p->vertex] && p->weight < vx[p->vertex].key)
            {
                parent[p->vertex] = nd.v;
                vx[p->vertex].key = p->weight;
                vx[p->vertex].v = p->vertex;
                q.push(vx[p->vertex]);
            }
            p = p->next;
        }
    }
}
int main()
{
    int a, b, w;
    while(scanf("%d%d", &m, &n), m)
    {
        Init();
        while(m--)
        {
            scanf("%d%d%d", &a, &b, &w);
            if(a != b)
            {
                Insert(a, b, w);
                Insert(b, a, w);
            }
        }
        Prim(1);
        int cnt = 0, ans = 0;
        for(int i = 1; i <= n; i++)
        {
            if(parent[i] == -1)
                cnt++;
            ans += vx[i].key;
        }
        if(cnt >= 2)
            printf("?\n");
        else
            printf("%d\n", ans);

    }
    return 0;
}

链接:1875 畅通工程再续

题目大意:依然是最小生成树,但是点换成了坐标表示,权值表示为两点的距离,求最小权值的和。

代码,邻接表:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
using namespace std;

#define maxn 110  //最大顶点个数
int n;       //顶点数,边数

struct arcnode  //边结点
{
    int vertex;     //与表头结点相邻的顶点编号
    double weight;     //连接两顶点的边的权值
    arcnode * next; //指向下一相邻接点
    arcnode() {}
    arcnode(int v,double w):vertex(v),weight(w),next(NULL) {}
};

struct vernode      //顶点结点,为每一条邻接表的表头结点
{
    int vex;    //当前定点编号
    arcnode * firarc;   //与该顶点相连的第一个顶点组成的边
}Ver[maxn];

void Init()  //建立图的邻接表需要先初始化,建立顶点结点
{
    for(int i = 1; i <= n; i++)
    {
        Ver[i].vex = i;
        Ver[i].firarc = NULL;
    }
}

void Insert(int a, int b, double w)  //尾插法,插入以a为起点,b为终点,权为w的边,效率不如头插,但是可以去重边
{
    arcnode * q = new arcnode(b, w);
    if(Ver[a].firarc == NULL)
        Ver[a].firarc = q;
    else
    {
        arcnode * p = Ver[a].firarc;
        q->next = p;
        Ver[a].firarc = q;
    }
}

struct node     //保存key值的结点
{
    int v;
    double key;
    friend bool operator<(node a, node b)   //自定义优先级,key小的优先
    {
        return a.key > b.key;
    }
};

#define INF 1e10    //权值上限
#define eps 1e-9
int parent[maxn];   //每个结点的父节点
bool visited[maxn]; //是否已经加入树种
node vx[maxn];      //保存每个结点与其父节点连接边的权值
priority_queue<node> q; //优先队列stl实现
void Prim(int s)    //s表示根结点
{
    for(int i = 1; i <= n; i++) //初始化
    {
        vx[i].v = i;
        vx[i].key = INF;
        parent[i] = -1;
        visited[i] = false;
    }
    vx[s].key = 0;
    q.push(vx[s]);
    while(!q.empty())
    {
        node nd = q.top();  //取队首,记得赶紧pop掉
        q.pop();
        if(visited[nd.v] == true)
            continue;
        visited[nd.v] = true;
        arcnode * p = Ver[nd.v].firarc;
        while(p != NULL)    //找到所有相邻结点,若未访问,则入队列
        {
            if(!visited[p->vertex] && p->weight < vx[p->vertex].key)
            {
                parent[p->vertex] = nd.v;
                vx[p->vertex].key = p->weight;
                vx[p->vertex].v = p->vertex;
                q.push(vx[p->vertex]);
            }
            p = p->next;
        }
    }
}
void Show()     //打印图的邻接表(有权值)
{
    for(int i = 1; i <= n; i++)
    {
        cout << Ver[i].vex;
        arcnode * p = Ver[i].firarc;
        while(p != NULL)
        {
            cout << "->(" << p->vertex << "," << p->weight << ")";
            p = p->next;
        }
        cout << "->NULL" << endl;
    }
}
struct Point
{
    int x, y;
}PT[maxn];
double dis(Point p1, Point p2)
{
    return 100.0*sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
            scanf("%d%d", &PT[i].x, &PT[i].y);
        Init();
        for(int i = 1; i < n; i++)
            for(int j = i+1; j <= n; j++)
            {
                double d = dis(PT[i], PT[j]);
                if(1000.00 - d > eps || d -100000.00 > eps)
                    continue;
                Insert(i, j, d);
                Insert(j, i, d);
            }
        Prim(1);
        int cnt = 0;
        double ans = 0;
        for(int i = 1; i <= n; i++)
        {
            if(parent[i] == -1)
                cnt++;
            ans += vx[i].key;
            if(cnt == 2)
                break;
        }
        if(cnt == 2)
            printf("oh!\n");
        else
            printf("%.1lf\n", ans);
    }
    return 0;
}

链接:1879

题目大意:这道题问题在于其中有些边已经存在,即有些路已经被修建好了,我们只需要将已经建好的边的权值置为0就必定会加入到最小生成树中

问题:这道题用Prim暂时还没过,应该是有什么坑数据,还在钻研

下面贴Kruskal的代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

#define maxn 110
struct eage
{
    int u, v, w;
}EG[5050];
int parent[maxn];
int n, m;
bool cmp(eage a, eage b)
{
    return a.w < b.w;
}
int Find(int x)
{
    if(parent[x] == -1) return x;
    return Find(parent[x]);
}
void Kruskal()
{
    for(int i = 1; i <= n; i++)
        parent[i] = -1;
    sort(EG+1, EG+m+1, cmp);
    int ans = 0;
    for(int i = 1; i <= m; i++)
    {
        int x = Find(EG[i].u);
        int y = Find(EG[i].v);
        if(x != y)
        {
            ans += EG[i].w;
            parent[x] = y;
        }
    }
    printf("%d\n", ans);
}
int main()
{
    int w, f;
    while(scanf("%d", &n), n)
    {
        m = n*(n-1)/2;
        for(int i = 1; i <= m; i++)
        {
            scanf("%d%d%d%d", &EG[i].u, &EG[i].v, &w, &f);
            if(f == 1)
                w = 0;
            EG[i].w = w;
        }
        Kruskal();
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值