算法竞赛入门经典 习题6-14

本文解析了UVa12118检查员困境问题,阐述了如何利用图论中的欧拉通路概念求解最短路径问题。文章详细介绍了算法实现过程,包括连通子图的划分、奇度数顶点计数及所需添加边数的计算。

UVa12118

Inspector’s Dilemma

每两个城市之间都有道路连通,检查员要通过指定的道路,途中可以经过其它未指定的道路。通过每条道路的时间是一样的,求出通过所有指定道路要花费的最小时间。

如果指定的道路属于不同的连通子图,则检查员需要经由一条额外的道路来连接不同的连通子图,总共需要经由的额外道路是连通子图的数量减1。

下面再来考虑连通子图中需要添加额外道路的数量。假设从任意的城市出发,尽量不重复的通过了尽可能多的道路,如果此时还有未通过的指定道路,那么检查员可以经由一条未指定的道路直接到达该指定道路的起点,也可以重复通过指定道路到达该指定道路的起点,显然前者花费的时间是不大于后者的,所以最优解应该是只通过一次指定道路的,那么问题就变成了在无向连通子图中添加若干条边来构建一条欧拉通路。

无向连通图中存在欧拉通路的充分条件是奇度数的顶点个数为2。由于每条边贡献一个出度和一个入度,所以图中总度数一定为偶数,据此可以推出奇度数顶点数量为偶数,同时每添加一条边就能减少两个奇度数的顶点,所以需要添加边的条数为奇度数顶点个数减2。

#include <iostream>
#include <vector>

using namespace std;

class Solution
{
public:
    Solution(const vector<vector<bool>>& graph, size_t E, size_t T) : graph(graph), visited(graph.size(), false), edges(E), time(T)
    {
        CountSubgraph();
        for (const vector<size_t>& subgraph : subgraphs)
        {
            size_t n = CountVertexWithOddDegree(subgraph);
            if (n > 2) edges += (n - 2) / 2;
        }
        edges += subgraphs.empty() ? 0 : subgraphs.size() - 1;
        time *= edges;
    }
    size_t TotalTraverseTime()
    {
        return time;
    }
private:
    vector<vector<bool>> graph;
    vector<bool> visited;
    vector<vector<size_t>> subgraphs;
    size_t edges, time;
    void CountSubgraph()
    {
        for (size_t i = 0; i < graph.size(); i++)
        {
            if (!visited[i]) {
                visited[i] = true;
                subgraphs.push_back(vector<size_t>());
                subgraphs.back().push_back(i);
                dfs(i, subgraphs.back());
                if (subgraphs.back().size() == 1) {
                    subgraphs.pop_back();
                }
            }
        }
    }
    void dfs(size_t curr, vector<size_t> &subgraph)
    {
        for (size_t adj = 0; adj < graph[curr].size(); adj++)
        {
            if (graph[curr][adj] && !visited[adj]) {
                visited[adj] = true;
                subgraph.push_back(adj);
                dfs(adj, subgraph);
            }
        }
    }
    size_t CountVertexWithOddDegree(const vector<size_t> &subgraph)
    {
        size_t num = 0;
        for (size_t i : subgraph)
        {
            size_t degree = 0;
            for (size_t j = 0; j < graph[i].size(); j++)
            {
                if (graph[i][j]) degree++;
            }
            if (degree % 2 == 1) num++;
        }
        return num;
    }
};

int main()
{
    int cases = 0;
    size_t V, E, T;
    while (cin >> V >> E >> T) {
        if (V == 0 && E == 0 && T == 0) break;
        vector<vector<bool>> graph(V, vector<bool>(V, false));
        size_t i, j;
        for (size_t e = 0; e < E; e++)
        {
            cin >> i >> j;
            graph[i - 1][j - 1] = graph[j - 1][i - 1] = true;
        }
        Solution solution(graph, E, T);
        cout << "Case " << ++cases << ": " << solution.TotalTraverseTime() << endl;
    }
}
/*
5 3 1
1 2
1 3
4 5
4 4 1
1 2
1 4
2 3
3 4
0 0 0
*/

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值