【问题标题】:C++: Fast algorithm for finding minimum sum of cycle edgesC++:寻找循环边的最小和的快速算法
【发布时间】:2015-07-05 01:02:20
【问题描述】:

我有一个无向加权图,想要找到所有循环边的最小总和。 这意味着如果我没有循环,答案是 0。 如果我有一个周期,答案是该周期的最小边缘权重。 如果我有多个周期,则为这些最小权重的总和。

我实现的算法使用某种 Prims 算法。 我只是添加最重的边缘,当形成一个循环时,将权重与答案值相加。

我认为这是正确的,因为我所有的测试用例都显示了正确的答案。

但一定是某个地方出错了,我只是还没找到。

struct connection {
    int a, b, cost;

    bool operator<(const connection rhs) const {
        return cost < rhs.cost || (cost == rhs.cost && (a < rhs.a || (a == rhs.a && b < rhs.b)));
    }
};

int n, m, researchers; // Amount of vertices, edges
std::list<connection> *adj; // Array of adjancency lists
std::list<int> *used;
std::set<connection> priorityQ;

void addEdge(int v, int w, int cost) {
    connection temp;
    temp.a = v;
    temp.b = w;
    temp.cost = cost;
    adj[v].push_back(temp);
    temp.a = w;
    temp.b = v;
    adj[w].push_back(temp);
}

bool isUsed(int u, int v) {
    for (std::list<int>::iterator it = used[u].begin(); it != used[u].end(); ++it) {
        int te = *it;
        if (te == v) return true;
    }
    return false;
}

void expand(int u) {
    for (std::list<connection>::iterator it = adj[u].begin(); it != adj[u].end(); ++it) {

        connection v = *it;

        if (isUsed(u, v.b)) continue;

        used[v.b].push_back(u);
        used[u].push_back(v.b);

        priorityQ.insert(v);
    }
}

void PrimR(int u, bool added[]) {
    added[u] = true;
    expand(u);
}

// Prim algorithm
void Prim(int u, bool added[]) {

    added[u] = true;

    expand(u);

    while (priorityQ.size() > 0) {
        connection now = *priorityQ.rbegin();
        priorityQ.erase(*priorityQ.rbegin());

        if (added[now.b]) {
            researchers += now.cost;
        }
        else {
            PrimR(now.b, added);

        }
    }

}

int main()
{

    int t;

    // loop over all test cases
    scanf("%d ", &t);
    for (int i = 1; i <= t; i++) {

        // read input nodes n, connections m
        scanf("%d %d", &n, &m);

        adj = new std::list<connection>[n];

        //read connections and save them
        for (int j = 0; j < m; j++) {
            int a, b, c;
            scanf("%d %d %d", &a, &b, &c);
            addEdge(a - 1, b - 1, c);
        }

        researchers = 0;

        // Use of prim with heaviest edges first
        bool *added = new bool[n];
        used = new std::list<int>[n];

        for (int j = 0; j < n; j++) {
            added[j] = false;
        }

        for (int j = 0; j < n; j++) {
            if (!added[j]) {
                Prim(j, added);
            }
        }


        // print desired output
        printf("Case #%d: %d\n", i, researchers);

        delete[] adj;
        delete[] added;
        delete[] used;
    }
    return 0;
}

你知道我做错了什么吗?

【问题讨论】:

标签: c++ algorithm graph cycle depth-first-search


【解决方案1】:

我没有考虑到,两个节点之间可能存在多个连接。

我的以下代码解决了这个问题:

struct connection {
    int a, b, cost, id;

    bool operator<(const connection rhs) const {
        return cost < rhs.cost || (cost == rhs.cost && id < rhs.id);
    }
};

int n, m, researchers; // Amount of vertices, edges
std::list<connection> *adj; // Array of adjancency lists
std::set<connection> priorityQ;

void addEdge(int v, int w, int cost, int id) {
    connection temp;
    temp.a = v;
    temp.b = w;
    temp.cost = cost;
    temp.id = id;
    adj[v].push_back(temp);
    temp.a = w;
    temp.b = v;
    adj[w].push_back(temp);
}

void deleteEdge(int v, int w, int id) {
    for (std::list<connection>::iterator it = adj[v].begin(); it != adj[v].end(); ++it) {
        if ((*it).id == id) {
            adj[v].erase(it);
            break;
        }
    }
    for (std::list<connection>::iterator it = adj[w].begin(); it != adj[w].end(); ++it) {
        if ((*it).id == id) {
            adj[w].erase(it);
            break;
        }
    }
}

void expand(int u) {
    for (std::list<connection>::iterator it = adj[u].begin(); it != adj[u].end(); ++it) {

        connection v;
        v.a = (*it).a < (*it).b ? (*it).a : (*it).b;
        v.b = (*it).a < (*it).b ? (*it).b : (*it).a;
        v.cost = (*it).cost;
        v.id = (*it).id;

        priorityQ.insert(v);
    }
}

void PrimR(int u, bool added[]) {
    added[u] = true;
    expand(u);
}

// Prim algorithm
void Prim(int u, bool added[]) {

    added[u] = true;

    expand(u);

    while (priorityQ.size() > 0) {
        connection now = *priorityQ.rbegin();
        priorityQ.erase(*priorityQ.rbegin());

        deleteEdge(now.a, now.b, now.id);

        if (added[now.b] && added[now.a]) {
            researchers += now.cost;
        }
        else if (added[now.b]) {
            PrimR(now.a, added);
        }
        else if (added[now.a]) {
            PrimR(now.b, added);
        }
    }

}

int main()
{

    int t;

    // loop over all test cases
    scanf("%d ", &t);
    for (int i = 1; i <= t; i++) {

        // read input nodes n, connections m
        scanf("%d %d", &n, &m);

        adj = new std::list<connection>[n];

        //read connections and save them
        for (int j = 0; j < m; j++) {
            int a, b, c;
            scanf("%d %d %d", &a, &b, &c);

            addEdge(a - 1, b - 1, c, j);
        }

        researchers = 0;

        // Use of prim with heaviest edges first
        bool *added = new bool[n];

        for (int j = 0; j < n; j++) {
            added[j] = false;
        }

        for (int j = 0; j < n; j++) {
            if (!added[j]) {
                Prim(j, added);
            }
        }


        // print desired output
        printf("Case #%d: %d\n", i, researchers);

        delete[] adj;
        delete[] added;
    }
    return 0;
}

【讨论】:

    【解决方案2】:

    你可以使用Floyd-Warshall算法。

    Floyd-Warshall 算法在所有顶点对之间找到最短路径。

    而这条通往(u,u) -&gt; (u,u) 的最短路径是您在考虑了所有可能的顶点u 之后找到的答案。

    算法运行时间为O(n^3)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-27
      • 1970-01-01
      • 2011-07-29
      • 1970-01-01
      • 1970-01-01
      • 2021-06-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多