今天照惯例a了几道搜索的题,我的天,不停的wa,代码也是相当长。。。。。。
今天主要是看了看图论最小生成树方面的算法
Prim算法和Kruskal算法
Prim算法
假设V是图中顶点的集合,E是图中边的集合,TE为最小生成树中的边的集合。
则prim算法通过以下步骤可以得到最小生成树:
1:初始化:U={u 0},TE={}。此步骤设立一个只有结点u 0的结点集U和一个空的边集TE作为最小生成树的初始行态,在随后的算法执行中,这个行态会不断的发生变化,直到得到最小生成树为止。
2:在所有u∈U,v∈V-U的边(u,v)∈E中,找一条权最小的边(u 0,v 0),将此边加进集合TE中,并将此边的非U中顶点加入U中。此步骤的功能是在边集E中找一条边,要求这条边满足以下条件:首先边的两个顶点要分别在顶点集合U和V-U中,其次边的权要最小。找到这条边以后,把这条边放到边集TE中,并把这条边上不在U中的那个顶点加入到U中。这一步骤在算法中应执行多次,每执行一次,集合TE和U都将发生变化,分别增加一条边和一个顶点,因此,TE和U是两个动态的集合,这一点在理解算法时要密切注意。
3:如果U=V,则算法结束;否则重复步骤2。可以把本步骤看成循环终止条件。我们可以算出当U=V时,步骤2共执行了n-1次(设n为图中顶点的数目),TE中也增加了n-1条边,这n-1条边就是需要求出的最小生成树的边。
kruskal算法
kruskal算法总共选择n- 1条边
所使用的贪婪准则是:从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合中。
注意到所选取的边若产生环路则不可能形成一棵生成树。
kruskal算法分e 步,其中e 是网络中边的数目。按耗费递增的顺序来考虑这e 条边,每次考虑一条边。
当考虑某条边时,若将其加入到已选边的集合中会出现环路,则将其抛弃,否则,将它选入。
步骤:
1).记Graph中有v个顶点,e个边
2).新建图Graphnew,Graphnew中拥有原图中相同的e个顶点,但没有边
3).将原图Graph中所有e个边按权值从小到大排序
4).循环:从权值最小的边开始遍历每条边 直至图Graph中所有的节点都在同一个连通分量中
if 这条边连接的两个节点于图Graphnew中不在同一个连通分量中
添加这条边到图Graphnew中
下面给出例题以及两种算法的模板
n个村,m条路,要用最少的钱把所有村连接起来
//使用Kruskal算法
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
const int N = 105;
int seed[N]; //构建并查集
int find_root(int x){
return seed[x] < 0? x : seed[x] = find_root(seed[x]);
}
int join_seed(int a, int b){
a = find_root(a), b = find_root(b);
if(a == b) return 0;
else seed[b] = a;
return 1;
}
struct E
{ // 定义边
int u, v, cost;
}edg[N*N];
int ecnt;
void init(){ // 初始化
memset(seed, -1, sizeof(seed));
ecnt = 0;
}
void add(int u, int v, int w){
edg[ecnt].u = u, edg[ecnt].v = v, edg[ecnt++].cost = w;
}
bool cmp(E a, E b)
{
return a.cost < b.cost;
}
int main()
{
int n;
while(scanf("%d", &n) != EOF && n != 0){
init();
int a, b, c;
for(int i = n*(n-1)/2; i > 0; --i)
{
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
sort(edg, edg+ecnt, cmp);
int ans = 0;
for(int i = 0; i < ecnt; ++i){
if(join_seed(edg[i].u, edg[i].v)) ans += edg[i].cost;
}
printf("%d\n", ans);
}
}
//使用prim算法
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
const int INF = ~0u>>2;
const int N = 105;
int G[N][N];
int dis[N];
void init(int n){
for(int i = 0; i <= n; ++i) dis[i] = INF;
for(int i = 0; i <= n; ++i)
for(int j = 0; j <= n; ++j)
G[i][j] = INF;
}
int prim(int rt, int n){
int vis[N] = {0};
dis[rt] = 0;
int res = 0;
for(int i = 0; i < n; ++i){
int min_u, min_dis = INF;
for(int j = 1; j <= n; ++j){ //找最小花费的点
if(vis[j] == 0 && dis[j] < min_dis){
min_dis = dis[j];
min_u = j;
}
}
vis[min_u] = 1;
res += min_dis;
for(int j = 1; j <= n; ++j){ //用最小点去更新其他点
if(vis[j] == 0 && dis[j] > G[min_u][j]){
dis[j] = G[min_u][j];
}
}
}
return res;
}
int main(){
int n;
while(scanf("%d", &n) != EOF && n != 0){
init(n);
for(int i = n*(n-1)/2; i >= 1; --i){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
if(G[a][b] > c) G[a][b] = G[b][a] = c;
}
int ans = prim(1, n);
printf("%d\n", ans);
}
}
基本上就这些,明天争取多做几道图论的题。|_.)