Telephone Lines(SPFA+多层图)

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;
}

猜你喜欢

转载自blog.csdn.net/qq_44643644/article/details/107899188