#define INFINITY INT_MAX //最大值∞
#define MAX_VERTEX_NUM 20 //最大顶点个数
typedef enum {DG,DN,UDG,UDN} Graphkind;
typedef struct ArdCell
{
VRType adj;
InfoType *info
}ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef struct
{
VertexType vexs[MAX_VERTEX_NUM]; //顶点向量
AdjMatrix arcs; //邻接矩阵
int vexnum,arcnum; //图的当前顶点数和弧数
GraphKind kind; //图的种类标志
}MGragh;
#define MAX_VERTEX_NUM 20 //最大顶点个数
typedef char* vertextType;
typedef struct ArcNode
{
int adjvex; //该弧指向的顶点的位置
struct ArcNode *nextarc; //指向下一条弧
InfoType* info; //该弧相关信息的指针
}ArcNode;
typedef struct VNode {
vertextType data; //结点信息
ArcNode* firstarc; //指向第一条依附在该顶点的弧的指针
}VNode, AdjList[MAX_VERTEX_NUM];
typedef struct
{
AdjList vertices;
int vexnum, arcnum;
int kind;
}ALGraph;
#define MAX_VERTEX_NUM 20
typedef struct ArcBox
{
int tailvex,heedvex;
struct ArcBox *hlink,*tlink;
InfoType *info;
}ArcBox;
typedef struct VexNode
{
VertexType data;
ArcBox *firstin,*firstout;
}VexNode;
typedef struct {
VexNode xlist[MAX_VERTEX_NUM];
int vexnum,arcnum; //有向图的当前顶点数和弧数
}OLGraph;
bool CreateDG(OLGraph& G)
{
cin>>G.vexnum>>G.arcnum;
//构造表头向量
for(int i=0;i<G.vexnum;++i)
{
cin>>G.xlist[i].data;
G.xlist[i].firstin = NULL;
G.xlist[i].firstout = NULL;
}
//输入各弧并构造十字链表
for(int k = 0;k<G.arcnum;++k)
{
int v1,v2,weight;
cin>>v1>>v2>>weight;
ArcBox* p = (ArcBox*)malloc(sizeof(ArcBox));
*p={v1,v2,G.xlist[v2].firstin,G.xlist[v1].firstout,weight};
G.xlist[v2].firstin = G.xlist[v1].firstout = p;
}
}
#define MAX_VERTEX_NUM 20
typedef enum {unvisited,visited} VisitIf;
typdef struct EBox
{
VisitIf mark; //访问标记
int ivex,jvex;
struct EBox *ilink,*jlink;
InfoType *info;
}EBox;
typedef struct VexBox
{
VertexType data;
EBox *firstedge;
}VexBox;
typedef struct
{
VexBox adjmulist[MAX_VERTEX_NUM];
int vexnum,edgenum;
}AMLGraph;
bool visited[MAX]; //访问标志数组
void DFSTraverse(Graph G)
{
int v;
for(v=0;v<G.vexnum;++v)
visited[v]=false;
for(v=0;v<G.vexnum;++v)
{
if(!visited[v])
DFS(G,v);
}
}
//从v开始深度遍历图
void DFS(Graph G,int v)
{
visited[v] = true;
visitFunc(v);
for(v的邻接结点)
{
if(!visited[w])
DFS(G,w);
}
}
bool visited[MAX]; //访问标志数组
void BFSTraverse(Graph G)
{
int v;
for(v=0;v<vexnum;++v)
visited[v] = false;
for(v=0;v<vexnum;++v)
{
if(!visited[v])
{
visited[v] = true;
EnQueue(Q,v);
BFS(G,v);
}
}
}
//从结点v开始广度遍历图
void BFS(Graph G,int v)
{
while(!QueueEmpty())
{
Dequeue(Q,v);
for(v的邻接结点)
{
if(!visited[w])
{
visited[w] = true;
Visit(w);
Enqueue(Q,w);
}
}
}
}
bool visited[MAX]; //访问标志数组
void DFSForest(Graph G)
{
int v;
bool first = true;
for(v=0;v<G.vexnum;++v)
{
visited[v]=false;
}
for(v=0;v<G.vexnum;++v)
{
if(!visited[v])
{
//分配孩子结点
p = (CSTree)malloc(sizeof(CSNode))
*p = {GetVex(G,w),nullptr,nullptr};
if(!T)
T=p;
else
q->nextsibling = p;
q = p;
DFS(G,v);
}
}
}
//从v开始深度遍历图
void DFS(Graph G,int v,CSTree& T)
{
visited[v] = true;
bool first = true;
visitFunc(v);
for(v的邻接结点)
{
if(!visited[w])
{
//分配孩子结点
p = (CSTree)malloc(sizeof(CSNode))
*p = {GetVex(G,w),nullptr,nullptr};
if(first)
{
T->lchild = p;
first = false;
}
else
{
q->nextsibling = p;
}
q = p;
DFS(G,w,q);
}
}
}
//辅助数组
typedef struct
{
int adjvex;
VRType lowhost;
}Closedge[MAX_VERTEX_NUM];
int minimum(Closedge cl,int size)
{
int min = INT_MAX;
int index;
for (int i = 0; i < size; ++i)
{
if (cl[i].lowhost != 0 && cl[i].lowhost <= min)
{
min = cl[i].lowhost;
index = i;
}
}
return index;
}
void MiniSpanTree_PRIM(MGragh G,int u)
{
Closedge closedge;
//初始化辅助数组
for (int i = 0; i < G.vexnum; ++i)
{
if(i!=u)
closedge[i] = { u,G.arcs[u][i].weight };
}
closedge[u] = { -1,0 }; //初始,U={u}
//选择剩余结点计算
for (int i = 0; i < G.vexnum-1; ++i)
{
int k = minimum(closedge,G.vexnum);
cout << closedge[k].adjvex << " " << k << endl;
closedge[k].lowhost = 0; //将k加入U
//更新辅助数组
for (int j = 0; j < G.vexnum; ++j)
{
if (closedge[j].lowhost > G.arcs[k][j].weight)
closedge[j] = { k, G.arcs[k][j].weight };
}
}
}
bool cmp(ArdCell a, ArcCell b)
{
return a.weight < b.weight;
}
void InitMFset(MGragh G, MFSet& set)
{
set.n = G.vexnum;
for (int i = 0; i <set.n; ++i)
{
set.nodes[i].parent = -1;
}
}
void MiniSpanTree_Kruskal(MGragh G)
{
int i, j,edgenum=0;
sort(edge, edge + G.arcnum, cmp);
InitMFset(G, set);
for (i = 0; i < G.arcnum; ++i)
{
//找到两个结点的根结点
int index1 = better_find_mfset(set, edge[i].u);
int index2 = better_find_mfset(set, edge[i].w);
//判断两个结点是否属于同一个连通分量
if (index1 == index2)
continue;
//合并
better_merge_mfset(set, index1, index2);
cout << edge[i].u << " " << edge[i].w << endl;
++edgenum;
if (edgenum == G.vexnum - 1)
break;
}
}
int count = 1;
int visited[MAX]; //访问标志数组
void FindArticul(Graph G)
{
visited[0] = 1; //设定图上0号顶点为生成树的根
int v;
for(1=0;v<G.vexnum;++v)
visited[v] = 0;
p = G.vertices[0].firstarc;
v = p->adjvex;
DFS(G,v); //从第v顶点出发深度优先查找关节点
if(count<G.vexnum) //生成树的根至少有两棵子树
{
cout<<0<<G.vertices[0].data; //根是关节点,输出
while(p->nextarc)
{
p = p->nextarc;
v = p->adjvex;
if(visited[v]==0)
DFS(G,v);
}
}
}
//从v开始深度遍历图,查找并输出关节点
void DFS(Graph G,int v)
{
visited[v] = min = ++count;
for(p=G.vertices[v].firstarc;p;p=p->nextarc)
{
w = p->adjvex; //得到v的邻接结点
//如果w未曾被访问
if(visited[w]==0)
{
DFSAriticul(G,w); //返回前求得low[w];
if(low[w]<min)
min = low[w];
if(low[w]>=visited[v])
cout<<v<<G.veretices[v].data; //输出关节点
}
else if(visited[w]<min) //w已访问,w是v在生成树上的祖先
min = visited[w];
}
low[v0]=min;
}
bool TopologicalSort(ALGraph G)
{
Stack s;
InitStack(s);
//首次
for (int i = 0; i < G.vexnum; ++i)
{
if (G.vertices[i].rudu == 0)
Push(s, i);
}
//对输出的结点计数
int count = 0;
while (!Empty(s))
{
int i = 0;
Pop(s, i);
cout << i << G.vertices[i].data;
++count;
//对该节点的弧进行删减,并将删除后没有前驱的结点入栈
for (p = G.vertices[i].firstarc; p; p = p->nextarc)
{
G.vertices[p.adjvex].rudu--;
if (G.vertices[p.adjvex].rudu == 0)
Push(s, k);
}
}
if (count < G.vexnum)
return false;
}
int ve[MAX_VERTEX_NUM]; //存储各顶点事件的最早发生时间
int vl[MAX_VERTEX_NUM]; //存储各顶点事件的最晚发生时间
Stack T;
bool TopologicalSort(ALGraph G)
{
Stack s;
InitStack(s);
memset(ve, 0, G.vexnum - 1);
//首次
for (int i = 0; i < G.vexnum; ++i)
{
if (G.vertices[i].rudu == 0)
Push(s, i);
}
//对输出的结点计数
int count = 0;
while (!Empty(s))
{
int i = 0;
Pop(s, i);
Push(T, i);
++count;
//对该节点的弧进行删减,并将删除后没有前驱的结点入栈
for (ArcNode* p = G.vertices[i].firstarc; p; p = p->nextarc)
{
int k = p->adjvex;
if (--G.vertices[k].rudu == 0)
Push(s, k);
if (ve[j] + *(p->info) > ve[k])
ve[k] = ve[j] + *(p->info);
}
}
if (count < G.vexnum)
return false;
}
bool CriticalPath(ALGraph G)
{
//先进行拓扑排序
if (!TopologicalSort(G))
return false;
for (int i = 0; i < G.vexnum; ++i)
vl[i] = ve[i];
//按拓扑逆序求各定点的vl值
while (!Empty(T))
{
int i = 0;
Pop(T, i);
for (ArcNode* p = G.vertices[i].firstarc; p; p = p->nextarc)
{
int k = p->adjvex;
dut = *(p->info);
if (vl[k] - dut < vl[j])
vl[j] = vl[k] - dut;
}
}
//求ee,和关键活动
for(j=0;j<G.vexnum;++j)
for (ArcNode* p = G.vertices[j].firstarc; p; p->nextarc)
{
int k = p->adjvex
dut = *(p->info);
ee = ve[j];
el = vl[k] - dut;
char tag = (ee == el) ? '*':' ';
cout << j << k << dut << ee << el << tag;
}
}
#define inf INT_MAX
int visited[100] = {0};
//源和目标结点
int src,dest;
int minpath = inf;
//cur是当前节点,dst是距离
void DFS(int cur,int dst)
{
//如果距离已经大于之前算过的,就没必要往下遍历了
if(minpath<dst) return;
if(cur == dest)
{
if(minpath>dst)
{
minpath = dst;
return;
}
}
for(int i = 0;i < n;++i)
{
if(visited[i]==0 && edge[cur][i]!=inf)
{
visited[i] = 1;
DFS(i,dst+edge[cur][i]);
visited[i] = 0;
}
}
}
#define nmax
#define inf INT_MAX
int dst[nmax];
int book[nmax];
void Dijstra(int u)
{
int i;
for(i=0;i<n;++i)
{
dst[i] = edge[u][i];
book[i] =0;
}
book[u] = 1;
for(i=0;i<n-1;++i)
{
int min = inf;
for(int j = 0;j<n-1;++j)
{
if(book[j]==0 && dst[j]<min)
{
min = dst[j];
u = j;
}
book[u] = 1;
//更新最短路径数组
for(int k=1;k<=n;k++)
{
if(book[k]==0&&dst[k]>dst[u]+edge[u][k]&&edge[u][k]<inf)
dst[k]=dst[u]+edge[u][k];
}
}
}
}
#include
using namespace std;
#define nmax 1001
#define inf 999999999
int n,m,s[nmax],e[nmax],w[nmax],dst[nmax];
int main ()
{
while(cin>>n>>m&&n!=0&&m!=0){
for(int i=1;i<=m;i++){
cin>>s[i]>>e[i]>>w[i];
}
for(int i=1;i<=n;i++)
dst[i]=inf;
dst[1]=0;
//主要算法:
for(int i=1;i<=n-1;++i){
for(int j=0;j<m;++j){
if(dst[e[j]]>dst[s[j]]+w[j]){
dst[e[j]]=dst[s[j]]+w[j];
}
}
}
int flag=0;
//判断是否存在负环
for(int i=0;i<m;++i){
if(dst[e[i]]>dst[s[i]]+w[i]){
flag=1;
}
}
if(flag) cout<<"此图有负权回路"<<endl;
else{
for(int i=1;i<=n;i++){
if(i==1) cout<<dst[i];
else cout<<' '<<dst[i];
}
cout<<endl;
}
}
}
虽然Bellman-Ford算法的思路很简洁,但是O(VE)的时间复杂度确实很高,在很多情况下并不尽如意。仔细思考后会发现,Bellman-Ford算法的每轮操作都需要操作所有边,显然这其中会有大量无意义的操作,严重影响了算法的性能。
于是注意到,只有当某个顶点u的dst[u]值改变时,从它出发的边的邻接点v的dst[v]值才有可能被改变。
由此可以进行一个优化:建立一个队列,每次将队首顶点u取出,然后对从u出发的所有边u->v进行松弛操作,如果d[v](这一轮的d[v]可能就是下一轮的d[u])改变了,如果v不在队列中,就把v加入队列。这样操作直到队列为空(图中没有从从源点可达的负环),或是某个顶点的入队次数超过V-1(说明图中存在从源点可达的负环)
void spfa()
{
queue<int>q;
q.push(1);
vis[1]=1;
while(!q.empty())
{
int t=q.front();
q.pop();
vis[t]=0;
for(int i=head[t];i!=-1;i=e[i].next){
int s=e[i].to;
if(dis[s]>dis[t]+e[i].val){
dis[s]=dis[t]+e[i].val;
if(vis[s]==0){
q.push(s);
vis[s]=1;
}
}
}
}
}
#define inf INT_MAX
void Floyd ()
{
for(int k=0;k<n;++k){
for(int i=0;i<n;i++i){
for(int 0=1;j<n;++j){
if(edge[k][j]<inf&&edge[i][k]<inf&&edge[i][j]>edge[i][k]+edge[k][j]){
edge[i][j]=edge[i][k]+edge[k][j];
}
}
}
}
最大流量问题是跟一个特殊的有向图有关,这个有向图称为流量网络,或者简称网络,它包含以下的特征:
(1)包含1个没有输入边的顶点;该顶点称为源点,用数字1标识
(2)包含1个没有输出边的顶点;该顶点称为汇点,用数字n标识
(3)每条有向边(i,j)的权重uij是一个正整数(这个数字表示该边所代表的链路把物质i送到j的数量上限),称为该边的容量
流量守恒要求:
该模型假设源点和汇点分别是物质流唯一的出发地和目的地,所有其他的顶点只能改变流的方向,但不能消耗或者添加物质。换句话说,进入中间顶点的物质总量必须等于离开的物质总量。
由流量守恒我们知道源点的总输出流等价于汇点的总输入流,我们称之为流的值,标记为v,这就是我们想要最大化的目标
因此,一个给定网络的(可行)流(flow)是实数值xij对边(i,j)的分配,使得网络满足流量守恒约束和容量约束
对于每条边(i,j)∈E来说,0<=xij<=uij
因此,最大流量问题可以用下面这个最优问题正式定义:
对于每条边(i,j)∈E来说,0<=xij<=uij
根据约束:对每个顶点(除源点和汇点)来说,进的流量=出的流量
使得源点的流达到最大化
我们用到了迭代改进的思想:
我们总是可以从流量0开始(也就是对于网络的每条边(i,j),都设置xij)。然后,在每次迭代时, 试着找到一条可以传输更多的流量的,从源点到汇点的路径。这样的路径被称为流量增益路径。如果找到了一条流量增益路径,我们沿着路径调整边上的流量,以得到更大的流量值,并试着为新的流量找到一条新的增益路径,如果不能找到流量增益路径,我们就认为当前流量已经是最优的了。这个解的最大流量问题的一般性模板被称为增益路径法,也被称为Ford-FulKerson。
为了求流量x的流量增益路径,我们需要考虑无向图中具有如下特征的任意连续顶点i和j:
对于一个给定的流量增益路径,设r是路径中所有前向边的流量增益路径,设r是路径中所有前向边的未使用容量rij和所有后向边的流量ji的最小值。很容易看出,如果我们在每条前向边的当前流量上增加r,在每条后向边的流量上减去r,就会得到一个可行流量。的确,设i是一条流量增益增益路径上的一个中间顶点。在顶点上存在前向边和后向边的4种可能组合:
无论哪种情况,在完成边上给出的流量调整以后,顶点i仍然满足流量守恒要求。
基于所有边的容量都是整数的假设,r也一定是一个正整数。因此,在增量路径法的每次迭代中,流量的值至少增加1。由于流量最大值的上界已经确定,增益路径法在有限次迭代后一定会停止。令人惊讶的是,最终的流量一定是最大化的,而且和增量路径的变化次序无关。
幸运的是,有若干生成流量增益路径的高效方法,它们都可以避免上例中显示的性能下降。其中最简单的一种方法利用广度优先查找,用数量最少的边来生成增益路径。这种增量路径法称为最短增益路径法或者先标记先扫描法。这里的标记意味着用两个记号来标记一个新的(未标记)的顶点。第一个标记指出从源点到被标记顶点还能增加多少流量。第二标记指出了另一个顶点的名字 ,就是从该顶点访问到被标记顶点的。方便起见也可以为第二个标记加上+或者-符号,用来分别指出该顶点是通过前向边还是后向边访问到的。因此,源点总是可以标记为∞,-。对于其他顶点, 则要按照下述方法计算它的标记:
具体算法如下
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define nmax 100
#define inf 999999
typedef struct
{
int x; //当前流量
int u; //总容量
}edge;
typedef struct
{
bool mark = false;
int l;
int lastV;
}v;
edge graph[nmax][nmax];
v vex[nmax];
int n, m;
int dest;
int ShortestAugmentingPath(int src)
{
int x = 0;
queue<int> q;
q.push(src);
vex[src].l = inf;
vex[src].mark = true;
while (!q.empty())
{
int cur = q.front();
q.pop();
//从i到j的每一条边————前向边
for (int i = 1; i <= n; ++i)
{
if (graph[cur][i].u != inf && !vex[i].mark)
{
int r = graph[cur][i].u - graph[cur][i].x;
//如果有可增加容量,则标记
if (r > 0)
{
vex[i].mark = true;
vex[i].l = min(vex[cur].l, r);
vex[i].lastV = cur;
q.push(i);
}
}
}
//从j到i的每一条————后向边
for (int i = 1; i <= n; ++i)
{
if (graph[i][cur].u != inf && !vex[i].mark)
{
if (graph[i][cur].x > 0)
{
vex[i].mark = true;
vex[i].l = min(vex[cur].l, graph[i][cur].x);
vex[i].lastV = -cur;
q.push(i);
}
}
}
//如果汇点被标记了,沿着找到的增益路径进行增益
if (vex[dest].mark)
{
int tmp = dest;
int l = vex[tmp].l;
while (tmp != src) //没有回到源点
{
//如果是前向边
if (vex[tmp].lastV > 0)
graph[vex[tmp].lastV][tmp].x += l;
else
graph[tmp][-vex[tmp].lastV].x -= l;
tmp = abs(vex[tmp].lastV);
}
//除了源点,擦去所有顶点的标记
for (int i = 2; i <= n; ++i)
{
vex[i].mark = false;
}
//清空队列
while (!q.empty())
{
q.pop();
}
//加入源点
q.push(src);
}
}
for (int i = 1; i <= n; ++i)
{
if (graph[src][i].u != inf)
{
x += graph[src][i].x;
}
}
return x;
}
int main()
{
cin >> n >> m>>dest;
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= n; ++j)
{
graph[i][j].x = 0;
graph[i][j].u = inf;
}
}
for (int i = 1; i <= m; ++i)
{
int u, v;
cin >> u >> v;
cin >> graph[u][v].u;
}
cout<<ShortestAugmentingPath(1);
return 0;
}
在一个二分图中,所有的顶点都可以分为两个不想交的集合V和U,两个集合大小不一定相等,但每条边都连接两个集合中各一个顶点。换句话说,一个二分图的顶点可以染成两种颜色,使得每条边两头顶点的颜色是不同的,这样的图也称为二色图。不难证明,当且仅当图中不存在奇数长度的回路时,图是二色图
设M是二分图G={V,U,E}的一个匹配。
一般来说,我们通过构造构造一条简单路径来增加当前匹配的规模。这条路径一头连接V中的自由顶点,另一头连接U中的自由顶点,路径上的边交替出现在E-M和M中。也就是说,路径上的第一条边不属于M,第二条边属于M,以此类推,直到最后一条不属于M的边。这种路径称为M的增益路径。
由于增益路径的长度总是为奇数,把位置为奇数的边加入M,把位置为偶数的边从M中删除,就可以生成一个新的匹配,该匹配比M多一条边,对匹配的这种调整称为增益
不断找增益路径,直到找不到增益路径后,就能得到最大匹配。
现在提供一个具体的算法来实现这个思路。该算法用一种类似广度优先查找的图遍历方法来搜索匹配M的一个增益路径,它从V或者U中选择一个集合,从该集合的所有自由顶点开始搜索(合理的做法是选择较小的顶点集合)。
回想一下,增益路径如果存在,它是一条连接V中自由顶点和U中自由顶点的奇数长度的路径,除非它只包含一条边,否则,它沿“之”字形,把V中的一个顶点和U中另一个顶点相连,然后再沿“之”字形,沿着M所定义的唯一一条可能边连回V,依次类推,直到遇到U中的一个自由顶点。根据这个现象,在对图进行类似广度优先查找遍历时,可以根据下面的规则来标记顶点:
更生动的理解可以参考这篇博客:算法讲解:二分图匹配
#define nmax 100
#define inf 999999
int line[nmax][nmax];
int girl[nmax], used[nmax];
int n,m;
bool found(int x)
{
for (int i = 1; i <= n; ++i)
{
if (line[x][i] && !used[i])
{
used[i] = 1;
if (girl[i] == 0 || found(girl[i]))
{
girl[i] = x;
return true;
}
}
}
return false;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= n; ++j)
{
line[i][j] = 0;
}
}
for (int i = 1; i <= m; ++i)
{
int u, v;
cin >> line[u][v];
}
memset(girl, 0, sizeof(girl));
//最大匹配数
int sum = 0;
for (int i = 1; i <= m; ++i)
{
memset(used, 0, sizeof(used));
if (found(i))
sum++;
}
return 0;
}
具体看这篇博客:
KM算法详解+模板
稳定婚姻讲的是什么问题呢?
婚介所登记了N位男孩和N位女孩,每个男孩都对N个女孩的喜欢程度做了排序,每个女孩都对N个男孩的喜欢程度做了排序,你作为月老,能否给出稳定的牵手方案?
稳定的定义:如果男孩i和女孩a牵手,但男孩i对女孩b更喜欢,而女孩b的男朋友j拼不过男孩i,则没有力量阻碍男孩i和女孩b的私奔,这即是不稳定的。
也就是说其实其本质是二分图匹配的一种特殊情况,图中每个男孩都能跟每个女孩进行匹配,每个女孩也能跟每个男孩进行匹配,只是优先级不同。
1962 年,美国数学家 David Gale 和 Lloyd Shapley 发明了一种寻找稳定婚姻的策略。不管男女各有多少人,不管他们各自的偏好如何,应用这种策略后总能得到一个稳定的婚姻搭配。具体算法描述如下:
第一轮,每个男人都选择自己名单上排在首位的女人,并向她表白。这种时候会出现两种情况:
第一轮结束后,有些男人已经有女朋友了,有些男人仍然是单身。
在第二轮追女行动中,每个单身男都从所有还没拒绝过他的女孩中选出自己最中意的那一个,并向她表白,不管她现在是否是单身。这种时候还是会遇到上面所说的两种情况,还是同样的解决方案。直到所有人都不再是单身。
using namespace std;
#define nmax 5
#define inf 999999
int match[nmax];
int man[nmax][nmax] = {
{0,0,0,0,0},
{0,2,3,1,0},
{0,2,1,3,0},
{0,0,2,3,1},
{0,1,3,2,0},
};
int woman[nmax][nmax] = {
{0,0,0,0,0},
{0,0,3,2,1},
{0,0,1,2,3},
{0,0,2,3,1},
{0,1,0,3,2},
};
int refuse[nmax][nmax];
int mark[nmax]; //判断男生是否心有所属
//女生i是否已经被选择
int choose[nmax];
bool Judgemark(int n )
{
for (int i = 1; i <= n; ++i)
if (mark[i] == 0)
return false;
return true;
}
void GaleShapley(int n)
{
while (!Judgemark(n))
{
//遍历男生
for (int i = 1; i <= n; ++i)
{
if (mark[i] != 0) { continue; }
//找出优先级最高且未曾拒绝过的女生
int min = inf;
int womanIndex = -1;
for (int j = 1; j <= n; ++j)
{
if (man[i][j] < min && refuse[i][j] == 0)
{
womanIndex = man[i][j];
womanIndex = j;
}
}
//如果女生还没有男生追求过,则接受追求
if (choose[womanIndex] == 0)
{
choose[womanIndex] = i;
mark[i] = 1;
}
//如果追求过
else
{
int FirstMan = choose[womanIndex];
//如果女生更喜欢原配,则拒绝他
if (woman[womanIndex][FirstMan] >= woman[womanIndex][i])
{
refuse[i][womanIndex] = 1;
}
else
{
choose[womanIndex] = i;
mark[i] = 1;
mark[FirstMan] = 0;
}
}
}
}
}
void Print(int N) {
for (int i = 1; i <= N; i++)
cout << "Boy " << i << " matches " << "Girl " << choose[i] << endl;
cout << endl;
}
int main()
{
memset(refuse, 0, sizeof(refuse));
memset(choose, 0, sizeof(choose));
GaleShapley(nmax);
Print(nmax);
return 0;
}
按照非专业的说法:
这个问题要求找出一条n个给定的城市间的最短路径,使我们在回到出发的城市之前,对每个城市都只访问一次。
专业的说法:
q求一个带权图的最短哈密顿回路。哈密顿回路即对图的每个顶点都只穿越一次的回路
即蛮力法,使用DFS深度遍历图,如果遇到有更小的方案则更新
#include
#include
#include
using namespace std;
#define nmax 105
#define inf 999999
int graph[nmax][nmax];
int mark[nmax];
int mindist = inf;
typedef struct
{
int dist;
vector<int> vec;
}path;
vector<int> vec;
vector<path> pa;
int n, m;
bool cmp(const path& a, const path& b)
{
return a.dist < b.dist;
}
void DFS(int cur, int dist) {
bool boundary = true;
for (int i = 0; i < n; ++i) {
if (mark[i] == 0 && graph[cur][i] != inf) {
boundary = false;
mark[i] = 1;
vec.emplace_back(i);
DFS(i, dist + graph[cur][i]);
vec.pop_back();
mark[i] = 0;
}
}
//如果到了边界
if (boundary && graph[cur][0]!=inf) {
//检查是不是遍历了全部景点
for (int i = 0; i < n; ++i) {
if (mark[i] == 0)
return;
}
if (mindist >= dist + graph[cur][0]) {
mindist = dist + graph[cur][0];
vec.emplace_back(0);
path p;
p.vec = vec;
p.dist = mindist;
pa.emplace_back(p);
vec.pop_back();
}
}
}
int main()
{
cin >> n >> m;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
graph[i][j] = inf;
}
}
for (int i = 0; i < m; ++i) {
int u, v;
cin >> u >> v;
cin>>graph[u][v];
graph[v][u] = graph[u][v];
}
memset(mark, 0, sizeof(mark));
mark[0] = 1;
vec.emplace_back(0);
DFS(0, 0);
sort(pa.begin(), pa.end(), cmp);
int dist = pa[0].dist;
cout << dist << endl;
for (auto& p : pa) {
if (p.dist != dist)
break;
cout << p.vec[0];
for (int i = 1; i < p.vec.size(); ++i) {
cout << "->" << p.vec[i];
}
cout << endl;
}
return 0;
}