您的位置:首页 > 其它

周赛1003题解 hdu 1598 find the most comfortable road (二分+bfs 或者 并查集)

2014-03-10 15:48 369 查看

find the most comfortable road

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 3145    Accepted Submission(s): 1334

[align=left]Problem Description[/align]
XX星有许多城市,城市之间通过一种奇怪的高速公路SARS(Super Air Roam Structure---超级空中漫游结构)进行交流,每条SARS都对行驶在上面的Flycar限制了固定的Speed,同时XX星人对 Flycar的“舒适度”有特殊要求,即乘坐过程中最高速度与最低速度的差越小乘坐越舒服 ,(理解为SARS的限速要求,flycar必须瞬间提速/降速,痛苦呀 ),

但XX星人对时间却没那么多要求。要你找出一条城市间的最舒适的路径。(SARS是双向的)。

 

[align=left]Input[/align]
输入包括多个测试实例,每个实例包括:

第一行有2个正整数n (1<n<=200)和m (m<=1000),表示有N个城市和M条SARS。

接下来的行是三个正整数StartCity,EndCity,speed,表示从表面上看StartCity到EndCity,限速为speedSARS。speed<=1000000

然后是一个正整数Q(Q<11),表示寻路的个数。

接下来Q行每行有2个正整数Start,End, 表示寻路的起终点。
 

[align=left]Output[/align]
每个寻路要求打印一行,仅输出一个非负整数表示最佳路线的舒适度最高速与最低速的差。如果起点和终点不能到达,那么输出-1。
 

[align=left]Sample Input[/align]

4 4
1 2 2
2 3 4
1 4 1
3 4 2
2
1 3
1 2

 

[align=left]Sample Output[/align]

1
0

 

[align=left]Author[/align]
ailyanlu
 

[align=left]Source[/align]
HDU 2007-Spring Programming Contest
- Warm Up (1)

题意:
从起点s到终点e找到一条路径,使这条路径中的最大边的权值-最小边的权值最小。
介绍两种方法:1、二分+bfs   2.并查集

思路1:(二分+bfs)
如果采用纯暴力的方式的话,枚举最大边、枚举最小边、bfs判断s能否到e,那么复杂度是q*m*m*n,(q为询问个数)太高了,不能过,于是想到枚举最小边,二分出在该最小边的情况下,能满足条件的最小的最大边,然后bfs判断s能否到e,复杂度就为q*m*log(m)*n,这个复杂度就能接受了。

ps1:bfs判断s能否到e,涉及到邻接表建图以及简单bfs搜索,大一的可以自己学一下,在以后的讲座中也会给大家讲解的。

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#pragma comment (linker,"/STACK:102400000,102400000")
#define maxn 205
#define MAXN 2005
#define mod 1000000009
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-6
typedef long long ll;
using namespace std;

int n,m,ans,cnt,tot,flag;
int a[2005],pp[205],q[205];  // pp-相当于邻接点的头指针  q-模拟队列
bool vis[205];  // 标记数组
int s,e,mi,ma,le,ri,mid;
struct Node
{
int v,w,next;
} edge[MAXN];  // 边

void addedge(int u,int v,int w) // 建图
{
cnt++;
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=pp[u];
pp[u]=cnt;
}
bool bfs1()
{
int i,j,t,u,v,head=0,tail=-1;
memset(vis,0,sizeof(vis));  // 标记数组清空
vis[s]=1;
q[++tail]=s; // 起点进队列
while(head<=tail) // 当队中有元素
{
u=q[head]; // 出队首
if(u==e) return true ; // 为e则能到达
head++;
for(i=pp[u];i;i=edge[i].next) // 访问u的邻接点
{
v=edge[i].v;
if(!vis[v]&&edge[i].w>=mi)
{
vis[v]=1;
if(v==e) return true ; // 为e则能到达
q[++tail]=v;
}
}
}
return false ;
}
bool bfs2() // 和bfs1基本一样
{
int i,j,t,u,v,head=0,tail=-1;
memset(vis,0,sizeof(vis));
vis[s]=1;
q[++tail]=s;
while(head<=tail)
{
u=q[head];
if(u==e) return true ;
head++;
for(i=pp[u];i;i=edge[i].next)
{
v=edge[i].v;
if(!vis[v]&&edge[i].w>=mi&&edge[i].w<=a[mid])
{
vis[v]=1;
if(v==e) return true ;
q[++tail]=v;
}
}
}
return false ;
}
void solve()
{
int i,j,t,q;
scanf("%d",&q);
sort(a+1,a+m+1); // 将边按照权值排序
while(q--)
{
scanf("%d%d",&s,&e);
ans=INF;        // 将答案出初始化为无穷大
for(i=1; i<=m; i++) // 枚举最小边
{
mi=a[i];
if(!bfs1()) break ;  // 看只走边>=最小边时s能否到达e 不能则后面的都不能到达了 退出循环
le=i;
ri=m;
while(le<ri)  // 二分最大值所在的数组下标
{
mid=(le+ri)>>1;
if(bfs2()) ri=mid;  // 看只走边在[a[le],a[ri]]范围内 s能否到达e
else le=mid+1;
}
ans=min(ans,a[le]-mi); // 更新答案
}
if(ans==INF) ans=-1;
printf("%d\n",ans);
}
}
int main()
{
int i,j,t;
while(~scanf("%d%d",&n,&m))
{
int u,v,w;
cnt=0;
memset(pp,0,sizeof(pp));
for(i=1; i<=m; i++)
{
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);  // 建图
addedge(v,u,w);
a[i]=w; // 记录权值
}
solve();
}
return 0;
}

思路2:(并查集)
将边按照权值排序,枚举最小边,然后枚举最大边,用并查集判断s是否能到e了(如果在同一集合就能到了)。

ps:不懂并查集的可以自己学一下,在以后的讲座中也会给大家讲解的。

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#pragma comment (linker,"/STACK:102400000,102400000")
#define maxn 1005
#define MAXN 2005
#define mod 1000000009
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-6
typedef long long ll;
using namespace std;

int n,m,ans,cnt,tot,flag;
struct Node
{
int u,v,w;
}edge[maxn];
int s,e;
int pre[205];

bool cmp(Node xx,Node yy) // 排序用的比较函数
{
return xx.w<yy.w;
}
int Find(int x) // 递归找到根节点
{
if(x==pre[x]) return x;
pre[x]=Find(pre[x]);
return pre[x];
}
void Merge(int x,int y) // 合并集合
{
int u=Find(x),v=Find(y);
if(u!=v) pre[u]=v;
}
void solve()
{
int i,j,t,q;
scanf("%d",&q);
sort(edge+1,edge+m+1,cmp); // 按照边权值排序
while(q--)
{
scanf("%d%d",&s,&e);
ans=INF;
for(i=1; i<=m; i++) // 枚举最小边
{
for(j=1;j<=n;j++) pre[j]=j;  // 并查集初始化
for(j=i;j<=m;j++)  // 枚举最大边
{
Merge(edge[j].u,edge[j].v);
if(Find(s)==Find(e)) break ;
}
if(j<=m) // 能到达则更新ans
{
ans=min(ans,edge[j].w-edge[i].w);
}
}
if(ans==INF) ans=-1;
printf("%d\n",ans);
}
}
int main()
{
int i,j,t;
while(~scanf("%d%d",&n,&m))
{
for(i=1; i<=m; i++)
{
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
}
solve();
}
return 0;
}


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: