
解决最大匹配问题简单,暴力的做法就是深搜,每一条边都搜一下,看看能不能搜,能搜的话就可以搜可以不搜,不能搜索就标定这边不能搜索然后接着往下走,然后找其中的最大的匹配即可。
而邮递员问题,这里用的是无向简单图,所以简单得多,检查一下是不是欧拉图,若是欧拉图,欧拉图由Hierholzer 算法来求,即欧拉图是由若干个圈构成的,那么最小的子问题就是把一个点的所有的圈都走一遍,走的时候删掉此边,那么一定能将整个欧拉图走完,然后倒序存入路径,这条欧拉路径就出来了,
若不是欧拉图就有说法了,由握手定理可以知道,存在偶数个奇数顶点,若是要回到原点,只能通过加重边的方式使得这个图变为欧拉图,不然不存在欧拉环游使得从起始点回到起始点,将其变成欧拉图之后用回欧拉图的方法即可,问题是怎么求最短的路径,当前的权是1(不影响),若要变为欧拉图,那么奇度顶点之间一定是有带权路径的,这样才能使得这俩点度数为偶数,若奇度顶点有k个,最少有k/2个路径,由于只需要求奇度顶点之间的路径并且使得其最小,我们可以把奇度顶点单独拿出来,若有路径的则有边,由于给出的图是连通的,那么一定有边,这样就会形成一个k阶完全图,而且为了求加的权最小,这些完全图中的边的值为最小带权路径的值,比如点1到点3有两条路径,那么一定要选最短的那条作为边,这样能确保之后走的权最小。
求出完全图之后,由于需要带权最小,那么任务就变成了求此完全图的最大匹配的最小带权组合,即存在若干个最大匹配,找权最小的那个,到这已经差不多了,然后将对应的路径加回原来的图中即可,这样得到的一定是欧拉图
参考代码如下:
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
//寻找所有的匹配
void dfs(int u,int n, int m, int grid[][1000], bool edgest[], bool pointst[], vector<vector<int>>& ans, vector<int>& setsize)
{
if (u>m)
{
vector<int>path;//将搜索到的边存进去
for (int i = 0; i < m; i++)
{
if (edgest[i])path.push_back(i);
}
setsize.push_back(path.size());
ans.push_back(path);
return;//深度搜索完毕,搜完边就行
}
int temp = 0;//临时用来计算此边关联的点有没有被搜索过
int edge[2] = { 0 };//用来看是哪两个点
for (int i = 0; i < n; i++)
{
if (grid[i][u]&&!pointst[i])
{
temp++;
if (temp == 1)edge[0] = i;
else edge[1] = i;
}
}
if (temp == 2)//说明这条边能用
{
edgest[u] = true;
pointst[edge[0]] = true;
pointst[edge[1]] = true;
dfs(u + 1, n, m, grid, edgest, pointst, ans, setsize);
edgest[u] = false;
pointst[edge[0]] = false;
pointst[edge[1]] = false;
dfs(u + 1, n, m, grid, edgest, pointst,ans, setsize);
}
else
{ //这边不能用也能搜,但没有上面那么多,因为不用改变状态
dfs(u + 1, n, m, grid, edgest, pointst, ans, setsize);
}
}
void pathdfs(int x,int graph[][100],int n,vector<int>&ans)
{
for (int y = 0; y < n; ++y)
{
if (graph[x][y] > 0)
{
graph[x][y]--;
graph[y][x]--;
pathdfs(y,graph,n,ans);
}
}
ans.push_back(x);
}
// 构造最短路径
void ConstructPath(const vector<int>& precursor, int start, int end, vector<int>& path)
{
path.clear();
int curr = end;
while (curr != start)
{ // 从终点回溯到起点
path.push_back(curr);
curr = precursor[curr];
}
path.push_back(start);
reverse(path.begin(), path.end()); // 反转路径,使其从起点到终点
}
// Dijkstra算法实现
void Dijkstra(int start, vector<int>& distance, vector<int>& precursor, vector<vector<int>> adjacencyMatrix)
{
// 定义无穷大的距离值
const int INF = INT_MAX;
int n = adjacencyMatrix.size();
distance.resize(n, INF); // 初始化距离数组为无穷大
distance[start] = 0; // 起点到自身的距离为0
precursor.resize(n, -1); // 初始化前驱节点数组,表示还未访问的节点前驱为-1
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq; // 小顶堆存储未访问节点
pq.push(make_pair(0, start)); // 将起点加入小顶堆
while (!pq.empty()) {
int dist = pq.top().first;
int currNode = pq.top().second;
pq.pop();
// 如果当前节点已经被访问过,跳过
if (dist > distance[currNode]) {
continue;
}
// 遍历当前节点的相邻节点
for (int i = 0; i < n; ++i)
{
if (adjacencyMatrix[currNode][i] != INF)
{ // 判断当前节点与相邻节点是否存在连接
int newDistance = dist + adjacencyMatrix[currNode][i];
if (newDistance < distance[i])
{ // 如果新的距离更小
distance[i] = newDistance;
precursor[i] = currNode; // 更新前驱节点
pq.push(make_pair(newDistance, i));
}
}
}
}
}
void postman(int n,int m,int grid[][1000])
{
//分三种情况,欧拉图,半欧拉图,非欧拉图
bool flag1 = false, flag2 = false, flag3 = false;//与上面情况对应
vector<int> path;//存放每个点度数
for (int i = 0; i < n; i++)
{
int sum = 0;
for (int j = 0; j < m; j++)
{
if (grid[i][j])
sum++;
}
path.push_back(sum);
}
int odd=0;//记录有多少个奇数顶点,同时存下奇数度顶点
vector<int> oddnum;
for (int i = 0; i < n; i++)
if (path[i] % 2 == 1)
{
odd++;
oddnum.push_back(i);
}
vector<int>result;//存放结果
//若odd为0则代表其为欧拉图,所有的度数均为偶数
//将关联矩阵转化为邻接矩阵和边矩阵
int graph[50][100] = { 0 }, ed[50][100] = { 0 };
for (int j = 0; j < m; j++)
{
vector<int>arr;
for (int i = 0; i < n; i++)
{
if (grid[i][j])arr.push_back(i);
}
int L = arr[0], r = arr[1];
graph[L][r] = 1;
graph[r][L] = 1;
ed[L][r] = j;
ed[r][L] = j;
}
if (odd != 0)
{
int dfsgridst[100] = { 0 };
for (int i=0; i < path.size(); i++)dfsgridst[path[i]] = i;
int oddgrid[100][100] = {0};//存奇数顶点的对应的最短路的矩阵
vector<vector<vector<int>>>oddpath(odd, vector<vector<int>>(odd));
// 定义无穷大的距离值
const int INF = INT_MAX;
vector<vector<int>> adjacencyMatrix(n, vector<int>(n, INF));
vector<int> ans;
//用adjacencyMatrix存邻接矩阵
for (int j = 0; j < m; j++)
{
vector<int>arr;
for (int i = 0; i < n; i++)
{
//若不能直接到达那么距离是无穷
if (grid[i][j])arr.push_back(i);
}
int L = arr[0], r = arr[1];
adjacencyMatrix[L][r] = 1;
adjacencyMatrix[r][L] = 1;
}
for (int i = 0; i < n; i++)adjacencyMatrix[i][i] = 0;
for (int i = 0; i < odd; i++)
{
int start = oddnum[i]; // 起点
vector<int> distance; // 保存最短距离的数组
vector<int> precursor; // 保存前驱节点的数组
Dijkstra(start, distance, precursor, adjacencyMatrix);
for (int k = 0; k < distance.size(); k++)//distance里面存起点到第k个点的最短距离
{
for (int z = 0; z < odd; z++)
{
if (oddnum[z] == k)
{
oddgrid[i][z] = distance[k];
}
}
}
for (int j = 0; j < odd; j++)
{
if (i == j)
{
oddpath[i][j].push_back(0);
continue;
}
vector<int> path; // 保存最短路径的数组
int end = oddnum[j]; // 终点
ConstructPath(precursor, start, end, path);
for (auto x : path)//存好路径
{
oddpath[i][j].push_back(x);
}
}
}
vector<vector<int>>ansdfs;
vector<int>setsize;
bool edgest[100] = { false }, pointst[100] = { false };
//将oddnum转化为关联矩阵,有n*(n-1)/2条边
int dfsgrid[20][1000] = { 0 };
int eddfs[100][100] = {0};
for (int i = 0; i < odd; i++)
for (int j = 0; j < odd; j++)eddfs[i][j] = -1;
int idx = 0;
for (int p = 0; p < odd; p++)
{
for (int z = p+1; z < odd; z++)
{
if (oddgrid[p][z])//若有边就将其存入关联矩阵当中
{
dfsgrid[p][idx] = 1;
dfsgrid[z][idx] = 1;
eddfs[p][z] = idx;
eddfs[z][p] = idx;
idx++;
}
}
}
dfs(0, odd, idx, dfsgrid, edgest, pointst, ansdfs, setsize);
int len = ansdfs.size();
int max = 0;//记录最大匹配的位置;
for (int i = 0; i < len; i++)
{
if (setsize[i] > max)
max = i;
}
int reslong = ansdfs[max].size();//记录找到的数组的长度
//将所有的最大匹配集输出
int mindfs = INF;
int flagdfs = 0;
for (int k = 0; k < len; k++)
{
if (setsize[k] == reslong)//若是最大匹配集的长度就将对应的路径加到矩阵当中
{
int sum = 0;
for (int i = 0; i < reslong; i++)
{
for (int edi = 0; edi < odd; edi++)
{
for (int edj = edi + 1; edj < odd; edj++)
{
if (ansdfs[k][i] == eddfs[edi][edj])
{
sum += oddpath[edi][edj].size()-1;
}
}
}
}
if (sum < mindfs)
{
mindfs = sum;
flagdfs = k;
}
}
}
vector<vector<int>>finalans;
for (int i = 0; i < reslong; i++)
{
for (int edi = 0; edi < odd; edi++)
{
for (int edj = edi + 1; edj < odd; edj++)
{
if (ansdfs[flagdfs][i] == eddfs[edi][edj])
{
finalans.push_back(oddpath[edi][edj]);
}
}
}
}
for (int i = 0; i < finalans.size(); i++)
{
for (int j = 0; j < finalans[i].size()-1; j++)
{
int Le = finalans[i][j], ri = finalans[i][j + 1];
graph[Le][ri] += 1;
graph[ri][Le] += 1;
}
}
}
vector<int> ans;
pathdfs(0, graph, n,ans);
int needlen = ans.size();
vector<int> turned;
printf("\n路径为{");
for (int i = 0; i < needlen-1; i++)
{
printf("e%d", ed[ans[i]][ans[i + 1]]+1);
}
printf("}");
}
int main()
{
int grid[100][1000] = { 0 };
int n, m;
vector<vector<int>>ans;
vector<int>setsize;
bool edgest[100] = { false }, pointst[100] = { false };
cin >> n >> m;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
cin >> grid[i][j];
}
dfs(0, n, m, grid, edgest, pointst, ans, setsize);
int len = ans.size();
int max = 0;//记录最大匹配的位置;
for (int i = 0; i < len; i++)
{
if (setsize[i] > max)
max = i;
}
int reslong = ans[max].size();//记录找到的数组的长度
//将所有的最大匹配集输出
for (int k = 0; k < len; k++)
{
if (setsize[k] == reslong)//若是最大匹配集的长度就输出
{
printf("最大匹配集为:{");
for (int i = 0; i < reslong; i++)
{
printf("e%d,", ans[k][i]);
}
printf("\b}\n");
}
}
postman(n,m,grid);
return 0;
}
//中国邮递员问题
//5 6
//1 0 0 1 0 1
//1 1 0 0 0 0
//0 1 1 0 0 0
//0 0 0 1 1 0
//0 0 1 0 1 1
本文讲述了如何使用深度优先搜索和Dijkstra算法解决邮递员问题,包括判断图是否为欧拉图,处理奇数顶点,构建最小权重的欧拉路径,以及通过最大匹配找到最短路径。
1679

被折叠的 条评论
为什么被折叠?



