POJ 1797 Heavy Transportation(最大生成树)

题目链接:http://poj.org/problem?id=1797

题目大意:给定n个顶点,以及m条边的描述,每条边的描述包括:起点、终点、权重。现在要从顶点1出发到达顶点n,求路径中能够承受的最大权重。

解题思路:读懂题意很重要,样例比较水,要去深入理解题目,同时注意输出格式。

1)本题要求出的是从顶点1到顶点n的所有可行路径中各边权值的最小值的最大值。即max(min(可行路径边))。很显然是最短路径的变形。

2)Prim算法:其实就是找一条带权路径,使得路径上最小的权值最大化,只要求一次最大生成树即可,没有什么复杂的算法,不过这个题要对最小生成树有更深的理解

prim算法:
1、设有一集合S,首先,将起点s(最后就成了最小生成树的根节点)放入集合,则S = {s}
2、找到距离集合S最近的一点u,加入S集合
3、通过点u,更新其余未进入集合S的点到集合S的距离
4、如果集合S外没有点了,结束,否则转到步骤2
其实整个过程中就是在找到一个距离起点集合S路径最小的点,加入进来,然后不断更新,这就像在建一棵连通树,在保证连通的情况下,不断的加入新的叶子节点,由于每次入的边权最小,所以最后建成的就是一棵最小生成树,它能保证起点到任意一点的路径上最小的权值最小化;同理,最大生成树就是在加点的时候找权值最大的加入就行了。

好多方法都能过,主要是题意的理解,SPFA,最短路,等。

最短路算法:

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
#include<stdlib.h>
#include<math.h>
#include<queue>
#include<map>
#include<stack>
#define mann 1008
#define INF 1000000001
typedef long long LL;
using namespace std;
int n,m;
int vis[mann],dis[mann];
int mp[mann][mann];
void dijstra(int cur)
{
    for(int i=1;i<=n;i++)
    {
        dis[i]=mp[cur][i];
        vis[i]=0;
    }
    dis[cur]=0;
    vis[cur]=1;
    int minn,pos,ans=INF;
    for(int i=1;i<n;i++)
    {
        minn=-2;
        for(int j=1;j<=n;j++)
        {
          if(!vis[j]&&dis[j]>minn)
          {
              minn=dis[j];
              pos=j;
          }
        }
        vis[pos]=1;
        for(int j=1;j<=n;j++)
        {
            if(!vis[j]&&dis[j]<min(mp[pos][j],dis[pos]))
                dis[j]=min(mp[pos][j],dis[pos]);
        }
    }
}
int main()
{
    int t,T=0;
    scanf("%d",&t);
    while(t--)
    {
        int u,v,w;
        memset(dis,0,sizeof(dis));
        memset(mp,0,sizeof(mp));//注意数据的初始化
        scanf("%d%d",&n,&m);
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d",&u,&v,&w);
                mp[u][v]=mp[v][u]=w;
        }
        dijstra(1);
        printf("Scenario #%d:\n%d\n\n",++T,dis[n]);
    }
}

prim算法:

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
using namespace std;
#define mann 1005
#define INF 0x3f3f3f3f
int aa[mann],mp[mann][mann];
int vis[mann],low[mann];
int n,m;
int prim()
{
    memset(vis,0,sizeof(vis));
    int pos=1,sum=0,minn;
    vis[pos]=1;
    low[pos]=0;
    for(int i=1;i<=n;i++)
    {
        if(i!=pos)
        low[i]=mp[pos][i];
    }
    for(int i=0;i<n;i++)
    {
        minn=-INF;
        for(int j=1;j<=n;j++)
        {
            if(!vis[j]&&low[j]>minn)
            {
                minn=low[j];
                pos=j;
            }
        }
        sum=min(minn,sum);
        if(pos==n)
            break;//并不一定要走完所有的点,走到终点就行
            vis[pos]=1;
            //printf("%d*%d\n",pos,minn);
        for(int j=1;j<=n;j++)
        {
            if(!vis[j]&&low[j]<mp[pos][j])
                low[j]=mp[pos][j];
        }
    }
    return sum;
}
int main()
{
    int t,T=0   ;
    scanf("%d",&t);
    while(t--)
    {
        memset(mp,-INF,sizeof(mp));
        int a,b,c;
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            mp[a][b]=mp[b][a]=c;
        }
        printf("Scenario #%d:\n%d\n\n",++T,prim());
    }
}

Kruskal:

#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<map>
#include<queue>
typedef long long LL;
using namespace std;
#define INF 0x3f3f3f3f
const int maxn=1000005;
int set[maxn];
int n,m;
struct node
{
    int u,v,w;
} c[maxn];
bool cmp(node aa,node bb)
{
    return aa.w>bb.w;
}
int find(int x)
{
    return set[x]==x?x:find(set[x]);
}
int merge(int x,int y)//判断是否属于同一个集合
{
    int fx=find(x);
    int fy=find(y);
    if(fx!=fy)
        return 1;//不属于
    return 0;//属于
}
int Kruskal()
{
    int maxx=INF;
    sort(c,c+m,cmp);
    for(int i=0; i<m; i++)
    {
        if(merge(c[i].v,c[i].u))//不属于同一个集合
        {
            maxx=min(maxx,c[i].w);//更新最小值
            set[find(c[i].v)]=find(c[i].u);//合并到同一集合
        }
         if(!merge(1,n))//判断1、n是否在同一集合,如果在,可以直接从1~n,跳出
            break;
    }
    return maxx;
}
void init()
{
    for(int i=1; i<=n; i++)
        set[i]=i;
}
int main()
{
    int t,T=0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init();
        for(int i=0; i<m; i++)
            scanf("%d%d%d",&c[i].u,&c[i].v,&c[i].w);
        int ans=Kruskal();
        printf("Scenario #%d:\n%d\n\n",++T,ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值