图论中的基本问题(未完)

1.最小生成树

a.kruskal 算法

在遇到最小生成树问题时,我个人偏好用kruskal算法,实在是觉得其中并查集的运用太经典了。

第一步:对所有的边从小到大排序;

第二步:依次选取n-1条不会产生回路的边;(用并查集判断添加边<i,j>是否产生回路)

模板:

代码
typedef struct
{
int i , j;
int dis;
}Node;
Node a[N
*N /2]; //C(2,N)
int p[N] , rank[N];

//快排
void QuickSort(Node *arr , int left , int right)
{
int i , j;
Node x , nTemp;

if(left >= right)
return;
else
{
i
= left; j = right + 1; x = arr[i];
while(1)
{
do i++; while(i < j && arr[i].dis < x.dis);
do j--; while(arr[j].dis > x.dis);
if(i > j) break;

nTemp
= arr[i]; arr[i] = arr[j]; arr[j] = nTemp;
}
nTemp
= arr[left]; arr[left] = arr[j]; arr[j] = nTemp;

QuickSort(arr,left,j
-1);
QuickSort(arr,j
+1,right);
}
}

//并查集基本操作
void MakeSet()
{
int i;

for(i = 0 ; i < N ; i++)
{
rank[i]
= 0 ;
p[i]
= i;
}
}

int Find(int x)
{
if(x != p[x])
{
p[x]
= Find(p[x]);
}
return p[x];
}

int Union(int x , int y)
{
int a , b;

a
= Find(x);
b
= Find(y);
if(a == b) return 0;
if(rank[a] > rank[b])
{
p[b]
= a;
}
else
{
p[a]
= b;
if(rank[a] == rank[b])
rank[b]
++;
}
return 1;
}

//主函数
int main(void)
{
scanf(...);
//读入t条边
QuickSort(a , 0 , t - 1);
MakeSet();
cnt
= sum = 0;
for(k = 0 ; k < t && cnt != n - 1 ; k++)
{
i
= a[k].i; j = a[k].j;
if(Union(i ,j))
{
sum
+= m[i][j];
cnt
+= 1;
}
}
printf(
"%d\n", sum);

return 0;
}

b.(朴素)prim算法

从任意节点开始,依次添加最小边到集合中去,类似Dijkstra算法

模板:

代码
int Prim()
{
int i , j , k , sum , min;

//从顶点0开始
for(i = 0 ; i < n ; i++)
{
lowcost[i]
= Graph[0][i];
}
lowcost[
0] = 0;

for(i = 1 , sum = 0 ; i < n ; i++)
{
min
= MAX;
for(j = 0 ; j < n ; j++) //这里可以用堆优化
{
if(lowcost[j] < min && lowcost[j])
{
k
= j;
min
= lowcost[j];
}
}
sum
+= lowcost[k];
lowcost[k]
= 0;

for(j = 0 ; j < n ; j++)
{
if(Graph[k][j] < lowcost[j])
{
lowcost[j]
= Graph[k][j];
}
}
}
return sum;
}

相关题目(比较基础):

Jungle Roads和Highways这两题直接套模板读入数据就OK了;

Eddy's picture和畅通工程再续这两题也是简单的模板套用,但是要注意的是:在求距离时不要开方(dis = (x1-x2) * (x1-x2) + (y1-y2) * (y1-y2)),只有在确定选取某条边的时候再开方,这样大大的减少开方次数(由n*(n-1)/2 降低到 n-1次,从而提高了效率);

Constructing Roads这题还是蛮有意思的,题目意思是在原有基础上(事先已经选取了若干条边)构建一棵“最小”生成树。我们可以这样思考:若原有基础中有x条边(去掉产生回路的边),那么我们只要按照原来算法取出剩余的n-1-x条边即可。

代码
#include <stdio.h>
#define N 101

typedef
struct
{
int i , j;
int dis;
}Node;
Node a[
50*N];

int p[N] , rank[N];

void QuickSort(Node *arr , int left , int right)
{
int i , j;
Node x , nTemp;

if(left >= right)
return;
else
{
i
= left; j = right + 1; x = arr[i];
while(1)
{
do i++; while(i < j && arr[i].dis < x.dis);
do j--; while(arr[j].dis > x.dis);
if(i > j) break;

nTemp
= arr[i]; arr[i] = arr[j]; arr[j] = nTemp;
}
nTemp
= arr[left]; arr[left] = arr[j]; arr[j] = nTemp;

QuickSort(arr,left,j
-1);
QuickSort(arr,j
+1,right);
}
}

void MakeSet()
{
int i;

for(i = 0 ; i < N ; i++)
{
rank[i]
= 0 ;
p[i]
= i;
}
}

int Find(int x)
{
if(x != p[x])
{
p[x]
= Find(p[x]);
}
return p[x];
}

int Union(int x , int y)
{
int a , b;

a
= Find(x);
b
= Find(y);
if(a == b) return 0;
if(rank[a] > rank[b])
{
p[b]
= a;
}
else
{
p[a]
= b;
if(rank[a] == rank[b])
rank[b]
++;
}
return 1;
}


int main(void)
{
int i , j , k , cnt , t , n , q;
int x , y , sum;
int m[N][N];

while(scanf("%d", &n) != EOF)
{

for(i = 0 , t = 0 ; i < n ; i++)
{
for(j = 0 ; j < n ; j++)
{
scanf(
"%d",&m[i][j]);
if(i < j)
{
a[t].i
= i;
a[t].j
= j;
a[t].dis
= m[i][j];
t
++;
}
}
}
QuickSort(a ,
0 , t - 1);

MakeSet();
scanf(
"%d", &q);
for(cnt = sum = i = 0 ; i < q ; i++)
{
scanf(
"%d%d", &x ,&y);
if(Union(x-1, y-1))
{
cnt
+= 1;
}
m[x
-1][y-1] = m[y-1][x-1] = 0;
}

for(k = 0 ; k < t && cnt != n - 1 ; k++)
{
i
= a[k].i;
j
= a[k].j;

if(Union(i ,j)) //不在一个集合
{
sum
+= m[i][j];
cnt
+= 1;
}
}
printf(
"%d\n", sum);
}
return 0;

Slim Span这题是寻找这么一棵“最小”生成树:cost = 最大权值的边 -  最小权值的边,求cost值最小的生成树,方法就是双重循环枚举所有生成树(暴力),然后更新最小值(内循环做了一点优化)。

代码
#include <stdio.h>
#define N 101
#define Min(a,b) ((a) < (b) ? (a) : (b))

typedef
struct
{
int i , j;
int dis;
}Node;

Node edge[N
*50];
int p[N] , rank[N];

void QuickSort(Node *arr , int left , int right)
{
int i , j;
Node x , nTemp;

if(left >= right)
return;
else
{
i
= left; j = right + 1; x = arr[i];
while(1)
{
do i++; while(i < j && arr[i].dis < x.dis);
do j--; while(arr[j].dis > x.dis);
if(i > j) break;
nTemp
= arr[i]; arr[i] = arr[j]; arr[j] = nTemp;
}
nTemp
= arr[left]; arr[left] = arr[j]; arr[j] = nTemp;

QuickSort(arr,left,j
-1);
QuickSort(arr,j
+1,right);
}
}


void MakeSet()
{
int i;

for(i = 0 ; i < N ; i++)
{
rank[i]
= 0 ;
p[i]
= i;
}
}

int Find(int x)
{
if(x != p[x])
{
p[x]
= Find(p[x]);
}
return p[x];
}

int Union(int x , int y)
{
int a , b;

a
= Find(x);
b
= Find(y);
if(a == b) return 0;
if(rank[a] > rank[b])
{
p[b]
= a;
}
else
{
p[a]
= b;
if(rank[a] == rank[b])
rank[b]
++;
}
return 1;
}

int main(void)
{
int n , m , i , j , t ,a , b;
int cnt , k , min;

while(scanf("%d%d", &n,&m) && n + m)
{
for(i = 0 ; i < m ; i++)
{
scanf(
"%d%d%d", &edge[i].i, &edge[i].j, &edge[i].dis);
}

if(m < n-1)
{
printf(
"-1\n");
}
else
{
min
= 99999999;
QuickSort(edge ,
0 , m - 1);
t
= m - n + 2;
for(i = 0 ; i < t ; i++)
{
MakeSet();
k
= edge[i].dis;
for(j = i , cnt = 0 ; j < m && cnt != n - 1; j++)
{
a
= edge[j].i;
b
= edge[j].j;
if(Union(a ,b))
{
if(edge[j].dis - k > min) break;
cnt
+= 1;
}
}
if(cnt == n - 1)
{
k
= edge[j-1].dis - edge[i].dis;
min
= Min(min, k);
}
}
min
== 99999999 ? printf("-1\n") : printf("%d\n", min);
}
}
return 0;
}

 

 

 

 

posted on 2010-05-15 19:46  DiaoCow  阅读(369)  评论(0编辑  收藏  举报

导航