http://poj.org/problem?id=2485
这是道最小生成树的题目,求的是最小生成树中最大边的权值。开始就想到用kruskal算法去做,
因为边是从小到大排序的,所以保证最后一条加入生成树的边的权值是最小生成树当中最大的。
Kruskal仅排序用的时间是O(mlog m),其中m 为n^2数量级的,为边的总数。
再说说prim算法,这个算法严格来说是今天才学的。在我看来,prim算法的精髓在于传递,建
立最小生成树的方法就是一个传递的过程,先将编号为0的点作为树根,然后找到离0最近的一点j,加
入生成树中,然后找离j最近的,每次都要更新lowc的值。找最大边的权值话就每次用加入的minc值
与max比较就行了。
下面是两种不同算法的代码:
/*
kruskal算法
Memory: 812K Time: 329MS
Language: C++ Result: Accepted
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
const int MAXN = 505;
int u[MAXN * MAXN], v[MAXN * MAXN], w[MAXN * MAXN], p[MAXN], r[MAXN * MAXN];
int e1, res, n, m;
int find_set( int x)
{
return p[x] == x ? x : ( p[x] = find_set( p[x]));
}
int cmp( const void *_p, const void *_q)
{
int *p = (int *)_p;
int *q = (int *)_q;
return w[*p] - w[*q];
}
void kruskal()
{
//res = 0;
for( int i = 0; i < n; i ++) p[i] = i;
for( int i = 0; i < m; i ++) r[i] = i;
qsort( r, m, sizeof(r[0]), cmp);
for( int i = 0; i < m; i ++)
{
int e = r[i]; int x = find_set(u[e]), y = find_set(v[e]);
if( x != y) { res += w[e]; p[x] = y; e1 = e;}
}
}
int main()
{
int T;
scanf( "%d", &T);
while( T --)
{
scanf( "%d", &n);
m = 0;
for( int i = 0; i < n; i ++)
for( int j = 0; j < n; j ++)
{
u[m] = i, v[m] = j;
scanf( "%d", &w[m ++]);
}
kruskal();
printf( "%d\n", w[e1]);
}
return 0;
}
/*
prim算法
Memory: 564K Time: 141MS
Language: C++ Result: Accepted
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
const int MAXN = 505;
bool vis[MAXN];
int w[MAXN][MAXN], lowc[MAXN], max, n, res;
const int INF = 0x3F3F3F3F;
void prim()
{
max = 0;
//res = 0;
int minc, p;
memset( vis, false, sizeof vis);
vis[0] = true;
for( int i = 1; i < n; i ++) lowc[i] = w[0][i];
for( int i = 1; i < n; i ++) {
minc = INF, p = -1;
for( int j = 0; j < n; j ++)
if( !vis[j] && minc > lowc[j]){
minc = lowc[j];
p = j;
}
vis[p] = true;
max = max > minc ? max : minc;
//res += minc;
// 更新lowc[j]的值。
for( int j = 0; j < n; j ++)
if( !vis[j] && lowc[j] > w[p][j])
lowc[j] = w[p][j];
}
}
int main()
{
int T;
scanf( "%d", &T);
while( T --)
{
scanf( "%d", &n);
for( int i = 0; i < n; i ++)
for( int j = 0; j < n; j ++)
scanf( "%d", &w[i][j]);
prim();
printf( "%d\n", max);
}
return 0;
}
今天想着将kruskal算法的写法改了下,记录树中边的数目,有n-1条边后跳出循环,省了那么一点时间。
/*Accepted 800K 282MS C++ 1373B 2012-07-24 08:40:52*/ #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; const int MAXN = 505; int p[MAXN], r[MAXN * MAXN], u[MAXN * MAXN], v[MAXN * MAXN], w[MAXN * MAXN]; int n, m, ret; bool cmp( const int i, const int j) { return w[i] < w[j]; } int find(int x) { return p[x] == x ? x : (p[x] = find(p[x])); } void init() { int i, j, val; m = 0; for( i = 1; i <= n; i ++) for( j = 1; j <= n; j ++) { scanf( "%d", &val); if(i == j) continue; u[m] = i, v[m] = j, w[m ++] = val; } } void kruskal() { int ans, cnt, e; ans = cnt = 0; int nx, ny, i, j; for( i = 1; i <= n; i ++) p[i] = i; for( i = 0; i < m; i ++) r[i] = i; sort( r, r + m, cmp); for( i = 0; i < m; i ++) { e = r[i]; nx = find(u[e]), ny = find(v[e]); if( nx != ny){ p[nx] = ny; ans += w[e]; cnt ++; if( n - 1 == cnt) { ret = w[e]; break; } } } } int main() { int T; scanf( "%d", &T); while( T --) { scanf( "%d", &n); init(); kruskal(); printf( "%d\n", ret); //printf( "\n"); } return 0; }