周赛1003例题 hdu 1598 find the most comfortable road (二分+bfs 或者 并查集)
周赛1003题解 hdu 1598 find the most comfortable road (二分+bfs 或者 并查集)
思路2:(并查集)
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
Problem Description
XX星有许多城市,城市之间通过一种奇怪的高速公路SARS(Super Air Roam Structure---超级空中漫游结构)进行交流,每条SARS都对行驶在上面的Flycar限制了固定的Speed,同时XX星人对 Flycar的“舒适度”有特殊要求,即乘坐过程中最高速度与最低速度的差越小乘坐越舒服 ,(理解为SARS的限速要求,flycar必须瞬间提速/降速,痛苦呀 ),
但XX星人对时间却没那么多要求。要你找出一条城市间的最舒适的路径。(SARS是双向的)。
但XX星人对时间却没那么多要求。要你找出一条城市间的最舒适的路径。(SARS是双向的)。
Input
输入包括多个测试实例,每个实例包括:
第一行有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, 表示寻路的起终点。
第一行有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, 表示寻路的起终点。
Output
每个寻路要求打印一行,仅输出一个非负整数表示最佳路线的舒适度最高速与最低速的差。如果起点和终点不能到达,那么输出-1。
Sample Input
4 4 1 2 2 2 3 4 1 4 1 3 4 2 2 1 3 1 2
Sample Output
1 0
Author
ailyanlu
Source
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; }