中文题目 7-10 公路村村通

题目描述:

现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。

 

输入格式:

输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N);

随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。

 

输出格式:

输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出−1,表示需要建设更多公路。

 
 
用Kruskal算法:
思路:单纯地贪心:
开始:将每个顶点都视作一棵树;
每次收录“最小权”的边,逐渐将树合并起来;【注意收录的是边】;
查看代码

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

constexpr int kInfty = ~(1 << 31);
constexpr int kMaxCity = 1024, kMaxRoad = 3*kMaxCity;

struct Edge {
    int vFrom, vTo, exps;
};
typedef int EdgePtr;

Edge edges[kMaxRoad];       //storage and hash;
EdgePtr edge_indices[kMaxRoad]; //for min heap; alike table-sort;

bool compare(EdgePtr a, EdgePtr b);

class MinHeap {  //elem is Edge;
public:
    using MHeapPtr = int;
    MinHeap(EdgePtr *l, int sz, bool (*pr)(EdgePtr, EdgePtr));
    EdgePtr pop();
private:
    void biuldUp();
    void percDown(MHeapPtr mhPtr);

    int size;
    EdgePtr *list;
    bool (*comp)(EdgePtr, EdgePtr);
};

class UCSet { //elem is Vertex;
public:
    using UCS_ptr = int;
    UCSet(int sz);
    bool unionSet(EdgePtr ePtr);
    bool checkSet(EdgePtr ePtr);
    int size();
private:
    UCS_ptr find(UCS_ptr x);
    
    int capacity;
    EdgePtr list[kMaxCity];
};

int cityN, roadM;

int Kruskal(MinHeap &unCollected, UCSet &MST);

int main(){
    cin >> cityN >> roadM;
    for (EdgePtr i = 0; i < roadM; ++i){
        cin >> edges[i].vFrom >> edges[i].vTo >> edges[i].exps;
        edge_indices[i] = i;
    }
    MinHeap minheap (edge_indices, roadM, compare);
    UCSet minSpanTree (cityN);
    int weighted_path_len = Kruskal(minheap, minSpanTree);
    cout << weighted_path_len;
    return 0;
}

int Kruskal(MinHeap &unCollected, UCSet &MST){
    /*initialization*/
    int WPL = 0;
    int eN_unC = roadM,  //edge num in uncollected set;
        eN_MST = 0,      //edge num in MST;
        eN_exp = cityN - 1;  //expected edge num;
    /*algorithm*/
    while ( eN_MST < eN_exp && eN_unC != 0){
        int crtE = unCollected.pop();
        --eN_unC; // == unCollected.size();

        /*if crt edge do not constitute circulation;
            unionSet() method will check before union opt;*/
        if ( MST.unionSet(crtE) ){//if already in one set, .unionSet() return false;
            ++eN_MST;
            WPL += edges[crtE].exps;
        }
        //else ignore crtE;
    }
    if (eN_MST < eN_exp)//Spanning Tree do not exist;
        return -1;
    return WPL;
}

bool compare(EdgePtr a, EdgePtr b){
    return edges[a].exps > edges[b].exps;
}

MinHeap::MinHeap(EdgePtr *l, int sz, bool (*pr)(EdgePtr, EdgePtr))
    : size{sz}, list{l}
{
    biuldUp();
}

EdgePtr MinHeap::pop(){
    EdgePtr ret = list[0];
    list[0] = list[--size];
    percDown(0);
    return ret;
}

void MinHeap::biuldUp(){
    MHeapPtr ptr = (size - 1) / 2;
    for (; ptr >= 0; --ptr)
        percDown(ptr);
    return;
}

void MinHeap::percDown(MHeapPtr mhPtr){
    MHeapPtr parent = mhPtr, child;
    EdgePtr tar = list[mhPtr];
    for (; (child = (parent + 1) * 2 - 1) <= size; parent = child){
        if (child != size && compare(list[child], list[child + 1]))//if child > child + 1;
            ++child;
        if (compare(tar, list[child]))
            list[parent] = list[child];
        else break;
    }
    list[parent] = tar;
    return;
}


UCSet::UCSet(int sz) : capacity{sz} {
    for (UCS_ptr i = 0; i <= sz; ++i)
        list[i] = -1;//each vertex is initially an independent tree;
}

bool UCSet::unionSet(EdgePtr ePtr){
    UCS_ptr x = find(edges[ePtr].vFrom);
    UCS_ptr y = find(edges[ePtr].vTo);
    if (x == y) return false; // no need to union;
    if (list[x] < list[y]){
        list[x] += list[y];
        list[y] = x;
    }
    else {
        list[y] += list[x];
        list[x] = y;
    }
    return true; //actually union has been done;
}

bool UCSet::checkSet(EdgePtr ePtr){
    UCS_ptr x = find(edges[ePtr].vFrom);
    UCS_ptr y = find(edges[ePtr].vTo);
    return x == y;
}

UCSet::UCS_ptr UCSet::find(UCS_ptr x){
    if (list[x] < 0)
        return x;
    return list[x] = find(list[x]);//with path compression;
}

 

用Prim算法:

dist含义:某一顶点距离当前MST的距离;所以当dist0,就意味着该点已被收录;
dist初始化:和源点s有直接边则为 E(s, V),否则正无穷;
最后检查:如果没有包含全部顶点,则生成树不成立,原因在于图不连通,也即dist为无穷大;
查看代码
#include<cstdio>
#include<vector>
using namespace std;

constexpr int kInfty = ~(1 << 31);
constexpr int kMaxCity = 1024, kMaxRoad = 3*kMaxCity;

int cityN, roadM;

int mgraph[kMaxCity][kMaxCity];

void read();
int Prim(int src);//return weight sum;

int main(){
    scanf("%d %d ", &cityN, &roadM);
    if (roadM == 0 || cityN == 0){
        printf("-1\n");
        return 0;
    }
    read();
    int resCost = Prim(cityN);//only once is sufficient, for min WPL is unique;
    if (resCost != kInfty)
        printf("%d\n", resCost);
    else
        printf("-1\n");
    return 0;
}

inline int getMinDist(int dist[]){
    int min = kInfty, resIdx = -1;
    for (int i = 1; i <= cityN; ++i)
        if (dist[i] != 0 && dist[i] < min){//uncollected && min dist;
            min = dist[i];
            resIdx = i;
        }
    return resIdx;//if not found return -1, which is initialized;
}

int Prim(int src){
    /*initialization*/
    //do not require output MST, no need for parent[];
    //min Cost counter and crt-collected-vertices counter;
    int minC = 0, vNum = 0;
    int dist[kMaxCity];
    for (int i = 0; i <= cityN; ++i)
        dist[i] = mgraph[src][i];//suppose in mgraph no edge is initialized to kInfty;
    /*algorithm*/
    dist[src] = 0;//collect source vertex;
    ++vNum;
    while (true){
        int crt = getMinDist(dist);
        if (crt == -1) break;
        minC += dist[crt];
        dist[crt] = 0;//collect it;
        ++vNum;
        for (int i = 1; i <= cityN; ++i){
            /*if uncollected && is adjacent vertex*/
            if (dist[i] != 0 && mgraph[crt][i] < kInfty){
                /*direct edge's weight == dist to crt MST*/
                if (mgraph[crt][i] < dist[i])
                    dist[i] = mgraph[crt][i];
                //parent[i] = crt;
            }
        }
    }
    if (vNum != cityN)//graph is un-connected;
        return kInfty;
    return minC;
}

void read(){
    /*initialization*/
    for (int i = 0; i <= cityN; ++i)
        for (int j = 0; j <= cityN; ++j)
            mgraph[i][j] = kInfty;
    /*read*/
    int v1, v2, wi;
    for (int i = 0; i < roadM; ++i){
        scanf("%d %d %d ", &v1, &v2, &wi);
        mgraph[v1][v2] = mgraph[v2][v1] = wi;
    }
    return;
}
posted @ 2022-04-02 12:04  HarukiZ  阅读(60)  评论(0编辑  收藏  举报