洛谷 P1948 [USACO08JAN]电话线Telephone Lines 二分 spfa dijkstra 最短路变形

题目链接:

https://www.luogu.org/problem/P1948

思路来源博客:

https://www.luogu.org/blog/five20/solution-p1948

思路:

1:二分+spfa,或者二分+dijkstra,求原点1到n的所有路中的第k+1长的路最小

2:为什么可以这样概括呢?因为题意中的答案要最小,我们贪心肯定要使k次免费的资格用完,那么最划算的方案肯定是拿最长的k条路使之免费,然后付第k+1长路的长度的钱,这样的贪心思路显然是正确的

3:我们首先二分第k+1长的路的长度(即答案),边界值l显然是0、r是1000000(题目中说边最长为1000000),然后关键是如何判断正确性

4:我们考虑简化问题,对于长度小于二分出的答案的线段,因为不需要付价钱,所以可以将其权值看作是0;同理,大于二分的值的路径,我们将长度看作1(意味着我需要使用1次免费的资格)

5:然后跑一遍spfa或者dijkstra,看到了n点的最短路的长度,如果大于k,则不行,缩小r范围继续二分;如果小于,则有可能更小,缩小l范围继续二分

特别提醒:

    1:这道题目还可以用分层图的思想来解,在看这道题目的时候可以再看看这种解法,以扩宽自己的思路,学习了二分+spfa或者dijkstra的解法之后深入理解分层图的思想:

https://blog.csdn.net/aiwo1376301646/article/details/100710842

代码:

1:二分+dijkstra

#include <bits/stdc++.h>

using namespace std;
const int maxn=1e3+1,inf=0x3f3f3f3f;
int n,m,k,a,b,c,d[maxn],ans,l,r,mid,temp;
vector<pair<int,int> >e[maxn];

inline void add_edge(int f,int t,int v)
{
    e[f].push_back(make_pair(t,v));
    e[t].push_back(make_pair(f,v));
}

inline bool dijkstra(int mid)
{
    memset(d,inf,sizeof(d));
    priority_queue<pair<int,int> >q;
    d[1]=0;
    q.push(make_pair(-d[1],1));
    while(!q.empty())
    {
        int now=q.top().second;
        q.pop();
        for(int i=0;i<e[now].size();i++)
        {
            int v=e[now][i].first;
            temp=e[now][i].second;
            if(temp>mid)
            {
                temp=1;
            }
            else
            {
                temp=0;
            }
            if(d[v]>d[now]+temp)
            {
                d[v]=d[now]+temp;
                q.push(make_pair(-d[v],v));
            }
        }
    }
    if(d[n]<=k)return true;
    else return false;
}

int main()
{
    ios::sync_with_stdio(0);
    cin>>n>>m>>k;
    for(int i=1;i<=m;i++)
    {
        cin>>a>>b>>c;
        add_edge(a,b,c);
    }
    l=0;
    r=1000000;
    ans=-1;
    while(l<=r)
    {
        mid=(l+r)/2;
        if(dijkstra(mid))
        {
            r=mid-1;
            ans=mid;
        }
        else
        {
            l=mid+1;
        }
    }
    cout<<ans<<endl;
    return 0;
}

2:二分+spfa

#include <bits/stdc++.h>

using namespace std;
const int maxn=1e3+1,inf=0x3f3f3f3f;
int n,m,k,a,b,c,d[maxn],ans,l,r,mid,temp,ing[maxn];
vector<pair<int,int> >e[maxn];

inline void add_edge(int f,int t,int v)
{
    e[f].push_back(make_pair(t,v));
    e[t].push_back(make_pair(f,v));
}

inline bool spfa(int mid)
{
    memset(d,inf,sizeof(d));
    memset(ing,0,sizeof(ing));
    queue<int>q;
    d[1]=0;
    ing[1]=1;
    q.push(1);
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        ing[now]=0;
        for(int i=0;i<e[now].size();i++)
        {
            int v=e[now][i].first;
            temp=e[now][i].second;
            if(temp>mid)
            {
                temp=1;
            }
            else
            {
                temp=0;
            }
            if(d[v]>d[now]+temp)
            {
                d[v]=d[now]+temp;
                if(ing[v])
                    continue;
                q.push(v);
                ing[v]=1;
            }
        }
    }
    return d[n]<=k;
}

int main()
{
    ios::sync_with_stdio(0);
    cin>>n>>m>>k;
    for(int i=1;i<=m;i++)
    {
        cin>>a>>b>>c;
        add_edge(a,b,c);
    }
    l=0;
    r=1000000;
    ans=-1;
    while(l<=r)
    {
        mid=(l+r)/2;
        if(spfa(mid))
        {
            r=mid-1;
            ans=mid;
        }
        else
        {
            l=mid+1;
        }
    }
    cout<<ans<<endl;
    return 0;
}
发布了117 篇原创文章 · 获赞 37 · 访问量 6594

猜你喜欢

转载自blog.csdn.net/aiwo1376301646/article/details/100712769