POJ 2395 Out of Hay

The cows have run out of hay, a horrible event that must be remedied immediately. Bessie intends to visit the other farms to survey their hay situation. There are N (2 <= N <= 2,000) farms (numbered 1..N); Bessie starts at Farm 1. She'll traverse some or all of the M (1 <= M <= 10,000) two-way roads whose length does not exceed 1,000,000,000 that connect the farms. Some farms may be multiply connected with different length roads. All farms are connected one way or another to Farm 1.

Bessie is trying to decide how large a waterskin she will need. She knows that she needs one ounce of water for each unit of length of a road. Since she can get more water at each farm, she's only concerned about the length of the longest road. Of course, she plans her route between farms such that she minimizes the amount of water she must carry.

Help Bessie know the largest amount of water she will ever have to carry: what is the length of longest road she'll have to travel between any two farms, presuming she chooses routes that minimize that number? This means, of course, that she might backtrack over a road in order to minimize the length of the longest road she'll have to traverse.
Input
* Line 1: Two space-separated integers, N and M.

* Lines 2..1+M: Line i+1 contains three space-separated integers, A_i, B_i, and L_i, describing a road from A_i to B_i of length L_i.
Output
* Line 1: A single integer that is the length of the longest road required to be traversed.
Sample Input
3 3
1 2 23
2 3 1000
1 3 43
Sample Output
43
Hint
OUTPUT DETAILS:

In order to reach farm 2, Bessie travels along a road of length 23. To reach farm 3, Bessie travels along a road of length 43. With capacity 43, she can travel along these roads provided that she refills her tank to maximum capacity before she starts down a road.


看到这个题,都想忍不住多多吐槽几句,前几日练了一些这种类型的(最小生成树)题,感觉挺简单的,都是一些模板题,可后来发现,有时候还是不能粗心大意的。这个题主要说的是找出一个最短路中最长的一条路。听得是不是略有些绕口,其实就是让你找出n-1条路,使其总长度最短,输出这些路中,最长的路。   一听是不是觉得用Krusal算法比较简单,是的,我用了两种方法来解决,一个是Krusal,一个是prim算法。这两个都是最小生成树,有些相似,却又有些不相似。

Krusal主要是以边的权值进行从小到大排序的,每次从剩余的边中选择权值比较小的且边的两个顶点不在同一个集合内(不产生回路的边),加到生成树中,直到n-1条边为止。

   Pirm(普利姆)算法和Dijkatra有点相似,Dijkatra使用一个一维数组dis来记录各个顶点到k点的距离,然后每次扫描数组dis,从中选出离远点最近的顶点,然后再通过这个点的所有的边能否更新k到各个顶点的距离(dis[m] > dis[j] + e[j][m]) 则更新 dis[m] = dis[j] + dis[j][m]      Prim 中数组dis 记录“生成树“到各个顶点的距离,也就是记录的最短距离,不是各个顶点到1号顶点的距离,而是每个顶点到任意“树“(已被选入生成树的顶点)的最短距离,即如果 dis[k] > e[j][k]

dis[k] = e[j][k].主要目的是靠近生成树     到最后 不得不多啰嗦几句,当你已经初始化的时候,在输入时,一定要先判断大小,然后再赋值  如 if(t3<e[t1][t2])                    //注意这个判断条件不能省略,因为刚开始的时候,已经将e数组全部附有初值,我就是栽到这的
                e[t1][t2] = e[t2][t1] = t3;

  有的时候  可以用Prim算法比较简单,有的时候用Krusal,一定要知道它们的优缺点,才能够很好的运用

//Kruskal  算法
#include<stdio.h>
#include<algorithm>        //对结构体进行排序的头文件
using namespace std;
int m,n,f[2010];

struct note
{
    int u,v,w;
}e[10010];               //注意数组的大小,不要和n范围给弄混了

int cmp(note a,note b)   //结构体进行从小到大排序
{
    return a.w < b.w;
}

void init()              //并查集初始化
{
    for(int i = 1; i <= n; i++)
        f[i] = i;
}

int getf(int v)         //并查集找祖先的递归函数
{
    if(f[v] == v)
        return v;
    else
    {                                   //路径压缩,在每次返回函数的时候,把它的值改为其祖先
        f[v] = getf(f[v]);
        return f[v];
    }
}

int merge(int v,int u)                 //并查集 合并两 子集合的函数
{
    int t1,t2;
    t1 = getf(v);
    t2 = getf(u);
    if(t1 != t2)                       //判断这两个点是否在同一个集合中
    {
        f[t2] = t1;
        return 1;
    }
    return 0;
}

void Kruskal()
{
    int maxx = -1;
    int num = 0;
    for(int i = 0; i < m; i++)          //从小到大枚举每一条边
    {
        if(merge(e[i].u,e[i].v))        //判断一条边的两个顶点是否已经通过,(判断是否在一个集合中)
        {
            num++;
            if(e[i].w > maxx)           //找出这些路中比较长的路程,存在maxx中
                maxx = e[i].w;
        }
        if(num == n-1)                  //选用n-1条边结束循环
            break;
    }
    printf("%d\n",maxx);
}

int main()
{
    while(~scanf("%d%d",&n,&m))         //n表示的是村庄的数目,  m表示的是道路数
    {
        for(int i = 0; i < m; i++)
            scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
        sort(e,e+m,cmp);                //两个村庄的距离进行从近到远排序
        init();                         //并查集初始化
        Kruskal();                      //克鲁斯卡尔
    }
    return 0;
}


//  prim  算法
#include<stdio.h>
#include<string.h>
#define inf 0x3f3f3f3f
int  e[2010][2010],dis[2010],minn;
int m,n,book[2010];
void prim()
{
    memset(book,0,sizeof(book));
    for(int i = 1; i <= n; i++)     //初始化dis数组,这里是1顶点到各个顶点的初始距离
        dis[i] = e[1][i];                   
    book[1] = 1;                    //标记是否生成树
    int num = 1;
    int w;
    int maxx = -1;
    while(num < n)      
    {
        minn = inf;
        for(int i = 1; i <= n; i++)
        {
            if(book[i] == 0 && dis[i] < minn)
            {
                minn = dis[i];
                w = i;
            }
        }
        book[w] = 1;
        if(minn > maxx)                           
            maxx = minn;
        num++;
        for(int k = 1; k <= n; k++)
            if(book[k] == 0 && dis[k] > e[w][k])    //找当前w点所有的边,到k点路程最短,更新生成树到每个非树顶点的距离
                dis[k] = e[w][k];
    }
    printf("%d\n",maxx);
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i = 1; i <= n; i++)             //初始化数据
            for(int j = 1; j <= n; j++)
                if(i == j)
                    e[i][j] = 0;
                else
                    e[i][j] = inf;
        int t1,t2,t3;
        for(int i = 1; i <= m; i++)
        {
            scanf("%d%d%d",&t1,&t2,&t3);
            if(t3<e[t1][t2])                    //注意这个判断条件不能省略,因为刚开始的时候,已经将e数组全部附有初值,我就是栽到这的
                e[t1][t2] = e[t2][t1] = t3;     //由于是无向图,所以需要将反向再储存一遍
        }
        prim();                                 //普里姆算法
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Qin7_Victory/article/details/75530673