Description
多年以后,笨笨长大了,成为了电话线布置师。由于地震使得某市的电话线全部损坏,笨笨是负责接到震中市的负责人。该市周围分布着N(1<=N<=1000)根据1……n顺序编号的废弃的电话线杆,任意两根线杆之间没有电话线连接,一共有p(1<=p<=10000)对电话杆可以拉电话线。其他的由于地震使得无法连接。
第i对电线杆的两个端点分别是ai,bi,它们的距离为li(1<=li<=1000000)。数据中每对(ai,bi)只出现一次。编号为1的电话杆已经接入了全国的电话网络,整个市的电话线全都连到了编号N的电话线杆上。也就是说,笨笨的任务仅仅是找一条将1号和N号电线杆连起来的路径,其余的电话杆并不一定要连入电话网络。
电信公司决定支援灾区免费为此市连接k对由笨笨指定的电话线杆,对于此外的那些电话线,需要为它们付费,总费用决定于其中最长的电话线的长度(每根电话线仅连接一对电话线杆)。如果需要连接的电话线杆不超过k对,那么支出为0.
请你计算一下,将电话线引导震中市最少需要在电话线上花多少钱?
Input
第一行包含三个数字n,p,k;
第二行到第p+1行,每行分别都为三个整数ai,bi,li。
Output
一个整数,表示该项工程的最小支出,如果不可能完成则输出−1.
Sample Input 1
5 7 1
1 2 5
3 1 4
2 4 8
3 2 3
5 2 9
3 4 7
4 5 6
Sample Output 1
4
分析:可以免费连k条边,那么可以使用k层图来做,免费一条边图就上升一层。我们无需构建k层图,只需在做SPFA的时候做一些修改,具体如何实现且看注释及代码。这道题初看不知道要用什么逻辑确定最优解,其实把免费连k条边这个条件去掉就明了了,没有这个条件时,就是普通的最短路问题:我们要尽量找小的边把1和N号结点连接,只不过这个题里只想知道这条最短路上最长的边;加上这个条件以后,就变成了尽量找小的边连接1和N号结点,给出这条最短路上第k+1大的边的大小。
#include <stdio.h>
#include <memory.h>
#include <queue>
#include <algorithm>
struct EDGE{
int v;//到达结点
int val;//边权
int next;//下一条边,为-1时表示没有
}edge[20010];//无权图,每条边存正存反
struct que_node{//队列元素类型
int x,side;//当前位置,处于第几层
que_node(){}
que_node(int x,int side)
{
this->x=x;
this->side=side;
}
que_node(const que_node &n)
{
this->x=n.x;
this->side=n.side;
}
};
int n,p,k,a,b,l;
int ans=0x7f7f7f7f;//答案,找最小值所以赋初值为最大值
int head[1010],cnt=0;
int d[1010][1010];//极端情况,所有结点连成一条线且都免费
bool visit[1010][1010]={0};//标记是否入队
inline void add_edge(int x,int y,int l)
{
edge[++cnt].v=y;
edge[cnt].val=l;
edge[cnt].next=head[x];
head[x]=cnt;
}
void spfa()
{
std::queue<que_node> que;
que.push(que_node(1,0));
visit[1][0]=true;
d[1][0]=0;//在起点处最大边为0
while(!que.empty())
{
que_node now(que.front());
que.pop();
visit[now.x][now.side]=false;
//走到一次终点就更新一次答案
if(now.x==n) ans=std::min(ans,d[n][now.side]);
//对于每条边,只有两种选择,即
for(int i=head[now.x];~i;i=edge[i].next)
{
//要么在当前层继续走,即当前边不免费
//只要发现有一条能到目标结点的路上的最大边更小
//就要更新
if(d[edge[i].v][now.side]>std::max(edge[i].val,d[now.x][now.side]))
{
d[edge[i].v][now.side]=std::max(edge[i].val,d[now.x][now.side]);
if(!visit[edge[i].v][now.side])
{
visit[edge[i].v][now.side]=true;
que.push(que_node(edge[i].v,now.side));
}
}
//要么往上走一层,即当前边免费
//既然当前边免费
//只要发现之前那层图上有一条到达当前结点now.x
//的路上的最大边更小,就要更新
if(now.side+1<=k&&d[edge[i].v][now.side+1]>d[now.x][now.side])
{
d[edge[i].v][now.side+1]=d[now.x][now.side];
if(!visit[edge[i].v][now.side+1])
{
visit[edge[i].v][now.side+1]=true;
que.push(que_node(edge[i].v,now.side+1));
}
}
}
}
//答案没被更新说明没有可行方案
if(ans==0x7f7f7f7f) ans=-1;
}
int main()
{
memset(head,-1,sizeof(head));
//d[x][side]存储的是免费了side条边后
//能走到x位置的所有路线中的最大边
//只要发现一条能走到x位置的路上最大边更小,就要更新
//所有给它所有元素赋最大值
memset(d,0x7f7f7f7f,sizeof(d));
scanf("%d %d %d",&n,&p,&k);
for(int i=1;i<=p;++i)
{
scanf("%d %d %d",&a,&b,&l);
add_edge(a,b,l);
add_edge(b,a,l);
}
spfa();
printf("%d",ans);
return 0;
}