题目链接: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;
}