一、Prime算法
1.算法的演进过程见下表:
2.代码如下(与 Dijkstra算法的差别非常非常小)
void DijkstraPath(MGraph g,int *dist,int *path,int v0) //v0表示源顶点
{
int i,j,k;
bool *visited=(bool *)malloc(sizeof(bool)*g.n);
for(i=0;i<g.n;i++) //初始化
{
if(g.matrix[v0][i]>0&&i!=v0)
{
dist[i]=g.matrix[v0][i];
path[i]=v0; //path记录最短路径上从v0到i的前一个顶点
}
else
{
dist[i]=INT_MAX; //若i不与v0直接相邻,则权值置为无穷大
path[i]=-1;
}
visited[i]=false;
}
path[v0]=v0;
dist[v0]=0; //这一句很重要
for(i=0;i<g.n;i++)
{
int min=INT_MAX;
int u=v0; //为了第一次循环而必须赋值
for(j=0;j<g.n;j++) //寻找未被扩展的权值最小的顶点
{
if(visited[j]==false&&dist[j]<min)
{
min=dist[j];
u=j;
}
}
visited[u]=true;
for(k=0;k<g.n;k++) //更新dist数组的值
{
if(visited[k]==false&&g.matrix[u][k]>0&&g.matrix[u][k]<dist[k]) //和Dijkstra算法的唯一区别
{
dist[k]=min+g.matrix[u][k];
path[k]=u;
}
}
}
}
Dijkstra算法传送门:http://blog.csdn.net/langqing12345/article/details/42490573
二、Kruskal算法——目前唯一用到并查集的算法
贪婪的连续地按照最小的权选择边,并且当所选的边不产生圈时就把它作为选定的边。不难发现,当且仅当新的边所连接的两个顶点之间已经有一条路径时(已经在集合中时)才会形成一个圈。
明确:1. 是以边而非Prime算法中的顶点作为操作对象的;
2. 对边的选择是以权重的大小为基础,对边权重进行排序,方法是堆排序(从大到小);
3. 每条边由三部分数据组成:两个顶点及边的权重;
4. 每次循环(循环的结束条件是所有的边被遍历或所有的顶点都已经被纳入在同一个集合中)得到的当前最小权重的边都是有两个顶点组成的,我们要通过得到的这条边得到组成它的两个顶点;
5. 通过不相交集ADT的Find操作可以知道这两个顶点是否已经(作为其他边的顶点)被选中,如果是,则舍弃,不是,就接受这条边并把相应的两个顶点纳入集合中;
5. 最初的不相交集ADT是每个顶点各为一个集合。最后是所有的顶点都合并到一个集合中。
算法的运行过程见下表(数据同Prime算法):
图例演变版本1:
图例演变版本2:
例题:POJ1789(这题可不是什么最长公共子序列!!!)
题意:
用一个7位的string代表一个编号,两个编号之间的distance代表这两个编号之间不同字母的个数。一个编号只能由另一个编号“衍生”出来,代价是这两个编号之间相应的distance,现在要找出一个“衍生”方案,使得总代价最小,也就是distance之和最小。
例如有如下4个编号:
aaaaaaa
baaaaaa
abaaaaa
aabaaaa
显然的,第二,第三和第四编号分别从第一编号衍生出来的代价最小,因为第二,第三和第四编号分别与第一编号只有一个字母是不同的,相应的distance都是1,加起来是3。也就是最小代价为3。
问题可以转化为最小生成树的问题。因为每两个结点之间都有路径,所以是完全图。
此题的关键是将问题转化为最小生成树的问题。每一个编号为图的一个顶点,顶点与顶点间的编号差即为这条边的权值,题目所要的就是我们求出最小生成树来。
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
using namespace std;
#define MAX 2005
char str[MAX][8];
int n,father[MAX],rank[MAX],k;//k为边数
struct node{
int st;
int en;
int we;
}edge[MAX*MAX/2];
int cmp(const void *a,const void *b)
{
return ((node*)a)->we-((node*)b)->we;
}
int Weight(int i,int j)
{
int m=0;
int w=0;
while(m<7)
{
if(str[i][m]!=str[j][m])
w++;
m++;
}
return w;
}
//并查集
void make_set()
{
int i;
for(i=0; i<MAX; i++)
{
father[i]=i;
rank[i]=0;
}
}
int find_set(int x)
{
if(father[x]!=x)
father[x]=find_set(father[x]);
return father[x];
}
bool Union(int x,int y)
{
int px=find_set(x);
int py=find_set(y);
if(px!=py)
{
if(rank[px]>rank[py])
{
father[py]=px;
}
else
{
if(rank[px]==rank[py])
rank[py]++;
father[px]=py;
}
return true;
}
return false;
}
int Kruskal()
{
int i,cost=0,nCount=0;
for(i=0; i<k; i++)
{
int start,end;
start=edge[i].st;
end=edge[i].en;
if(Union(start,end)) //Union的操作包含了find
{
cost+=edge[i].we;
}
}
return cost;
}
int main()
{
int i,j;
freopen("acm.txt","r",stdin);
while(scanf("%d",&n)!=EOF && n) //当n=0时结束输入和下面的运算
{
make_set();
getchar();
for(i=0; i<n; i++)
{
scanf("%s",str[i]);
}
k=0;
for(i=0; i<n-1; i++) //生成完全树
{
for(j=i+1; j<n; j++)
{
edge[k].st=i; //记录顶点编号
edge[k].en=j;
edge[k++].we=Weight(i,j);
}
}
qsort(edge,k,sizeof(node),cmp); //k是总的边数,对边的权重进行排序
printf("The highest possible quality is 1/%d.\n",Kruskal( ));
}
return 0;
}
参考资料:Weiss《数据结构与算法分析》
《算法设计与分析基础》
http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html