这道题难在要在一定条件下求得最短路。对车载高度进行二分,求出较高车载高度的情况下的最短路。通过判断是否能形成最短路来不断调整车载高度,如果在该车载高度情况下不能形成最短路则调低车载高度,否则调高车载高度。
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int Max=1200;
const int INF=1e9;
int n,m,star,endd;
int d[Max],Map[Max][Max],height[Max][Max]; //Map用来存放路长,height存放路限高
bool vis[Max]; //vis标记该点是否已经访问过
void init()
{
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
{
Map[i][j]=INF; //将任意两点间路长初始化无穷
height[i][j]=0; //路限高初始化为0
}
}
bool dijstra(int lim)
{
memset(vis,0,sizeof(vis)); //将所有点都初始化为未访问过
for(int j=1; j<=n; j++)
{
if(lim<=height[star][j]) //将与起点连通且该路限高大于车高的路更新
{
d[j]=Map[star][j];
}
else
{
d[j]=INF; //其余的路设为不通
}
}
vis[star]=1; //标记经过该点
for(int k=0; k<n; k++) //循环n次
{
int x=0,mm=INF; //将x设为不可能更新到的值,用来判断是否有与起点连通的路
for(int i=1; i<=n; i++)
{
//不可以是<=mm,如果有等号,则如果没有与源点的通路,就会一直更新到n,影响判断x,如果终点为n,则会一直逼近车载限高
if(!vis[i]&&d[i]<mm) //找出未访问过的点到源点最小的节点x
{
mm=d[x=i];
}
}
if(x==0) //说明起止点间没通路,需调低车载高度
return false;
else if(x==endd) //说明找到最短路,需调高车载高度
return true;
vis[x]=1;
for(int i=1; i<=n; i++)
{
if(!vis[i]&&height[x][i]>=lim) //如果x到未访问的点之间路的限高大于车载高度
{
d[i]=min(d[i],d[x]+Map[x][i]); //更新源点到未访问的点的路径长度
}
}
}
}
int main()
{
int t=0;
while(scanf("%d%d",&n,&m)!=EOF&&(n+m)!=0)
{
init();
int aa,bb,cc,dd;
for(int i=1; i<=m; i++)
{
scanf("%d%d%d%d",&aa,&bb,&cc,&dd);
if(cc==-1)
cc=INF;
Map[aa][bb]=Map[bb][aa]=dd; //双边处理
height[aa][bb]=height[bb][aa]=cc;
}
int limit,mid,left,right,ans;
scanf("%d%d%d",&star,&endd,&limit); //起点终点和车载的限高
left=1,right=limit,ans=INF;
while(left<=right) //跳出循环时的right即为最优车载高度
{
mid=(left+right)/2; //对车载高度二分,求出能达到最高车载高度
if(dijstra(mid)) //如果返回true,说明有起止点间的最短路
{
left=mid+1; //提高车载高度
ans=d[endd]; //记录路径长度
}
else
{
right=mid-1; //降低车载高度
}
}
if(t)
printf("\n");
printf("Case %d:\n",++t);
if(ans==INF) //ans值最终没变说明起止点不通
printf("cannot reach destination\n");
else
{
printf("maximum height = %d\n",right);
printf("length of shortest route = %d\n",ans);
}
}
return 0;
}
下面这个是用SPFA做的跟dijkstra的处理方法基本类似。
#include<stdio.h>
#include<iostream>
#include<queue>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAX=999999999;
const int edge_max=21000; //边的最大值
const int point_max=1110; //点的最大值
struct node
{
int en;
int height;
int lenth;
int next;
} edge[edge_max];
int pos_in_edge[point_max]; //在边中的位置
int dis[point_max],n,m,Star,End,limit;
bool vis[point_max];
queue<int>Q;
void init()
{
memset(pos_in_edge,-1,sizeof(pos_in_edge)); //初始化表头
int positn=1,sta,enb,lenc,heid;
for(int i=0; i<m; i++)
{
cin>>sta>>enb>>heid>>lenc;
if(heid==-1)
heid=MAX;
edge[positn].en=enb;
edge[positn].lenth=lenc;
edge[positn].height=heid;
edge[positn].next=pos_in_edge[sta];
pos_in_edge[sta]=positn++;
swap(sta,enb); //做双边处理
edge[positn].en=enb;
edge[positn].lenth=lenc;
edge[positn].height=heid;
edge[positn].next=pos_in_edge[sta];
pos_in_edge[sta]=positn++;
}
cin>>Star>>End>>limit;
}
void SPFA(int lim)
{
memset(vis,0,sizeof(vis));
fill(dis,dis+point_max,MAX); //dis数组初始化为最大
dis[Star]=0; //处理源点
vis[Star]=true;
Q.push(Star);
while(!Q.empty())
{
int e_st_p=Q.front(); //边的起点
vis[e_st_p]=false;
Q.pop();
for(int j=pos_in_edge[e_st_p]; j!=-1; j=edge[j].next)
{
if(edge[j].height<lim)
continue;
int e_en_p=edge[j].en; //边的终点
if(dis[e_en_p]>edge[j].lenth+dis[e_st_p]) //松弛操作
{
dis[e_en_p]=edge[j].lenth+dis[e_st_p];
if(!vis[e_en_p])
{
Q.push(e_en_p);
vis[e_en_p]=true;
}
}
}
}
}
int main()
{
int t=0;
while(scanf("%d%d",&n,&m)!=EOF&&(n+m)!=0)
{
init();
int left=1,right=limit,ans=MAX,mid;
while(left<=right)
{
mid=(left+right)/2;
SPFA(mid);
if(dis[End]!=MAX)
{
left=mid+1;
ans=dis[End];
}
else
{
right=mid-1;
}
}
if(t)
printf("\n");
printf("Case %d:\n",++t);
if(ans==MAX)
printf("cannot reach destination\n");
else
{
printf("maximum height = %d\n",right);
printf("length of shortest route = %d\n",ans);
}
}
return 0;
}