POJ 2485-Highways

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;

}

 

 



你可能感兴趣的:(poj)