Cpp環境【POJ3622】【Usaco2008 Jan Silver】【Vijos1874】Telephone Lines架設電話線

Description  【問題描述】

Farmer John wants to set up a telephone line at his farm. Unfortunately, the phone company is uncooperative, so he needs to pay for some of the cables required to connect his farm to the phone system.

There are N (1 ≤ N ≤ 1,000) forlorn telephone poles conveniently numbered 1..N that are scattered around Farmer John’s property; no cables connect any them. A total of P (1 ≤ P ≤ 10,000) pairs of poles can be connected by a cable; the rest are too far apart.

The i-th cable can connect the two distinct poles Ai and Bi, with length Li (1 ≤ Li ≤ 1,000,000) units if used. The input data set never names any {Ai, Bi} pair more than once. Pole 1 is already connected to the phone system, and pole N is at the farm. Poles 1 and N need to be connected by a path of cables; the rest of the poles might be used or might not be used.

As it turns out, the phone company is willing to provide Farmer John with K (0 ≤ K < N) lengths of cable for free. Beyond that he will have to pay a price equal to the length of the longest remaining cable he requires (each pair of poles is connected with a separate cable), or 0 if he does not need any additional cables.

Determine the minimum amount that Farmer John must pay.


  FJ打算將電話線引到自己的農場,但電信公司並不打算爲他提供免費服務。於是,FJ必須爲此向電信公司支付一定的費用。
  FJ的農場周圍分佈着N(1 <= N <= 1,000)根按1..N順次編號的廢棄的電話線杆,任意兩根電話線杆間都沒有電話線相連。一共P(1 <= P <= 10,000)對電話線杆間可以拉電話線,其餘的那些由於隔得太遠而無法被連接。第i對電話線杆的兩個端點分別爲A_i、B_i,它們間的距離爲L_i (1 <= L_i <= 1,000,000)。數據中保證每對{A_i,B_i}最多隻出現1次。編號爲1的電話線杆已經接入了全國的電話網絡,整個農場的電話線全都連到了編號爲N的電話線杆上。也就是說,FJ的任務僅僅是找一條將1號和N號電話線杆連起來的路徑,其餘的電話線杆並不一定要連入電話網絡。
  經過談判,電信公司最終同意免費爲FJ連結K(0 <= K < N)對由FJ指定的電話線杆。對於此外的那些電話線,FJ需要爲它們付的費用,等於其中最長的電話線的長度(每根電話線僅連結一對電話線杆)。如果需要連結的電話線杆不超過K對,那麼FJ的總支出爲0。
  請你計算一下,FJ最少需要在電話線上花多少錢。

Input  【輸入格式】
  • Line 1: Three space-separated integers: N, P, and K
  • Lines 2..P+1: Line i+1 contains the three space-separated integers: Ai, Bi, and Li

  第1行: 3個用空格隔開的整數:N,P,以及K
  第2..P+1行: 第i+1行爲3個用空格隔開的整數:A_i,B_i,L_i

Output  【輸出樣例】

* Line 1: A single integer, the minimum amount Farmer John can pay. If it is impossible to connect the farm to the phone company, print -1.


  輸出一行一個整數,爲FJ在這項工程上的最小支出。如果任務不可能完成,輸出-1。

Sample Input  【輸入樣例】

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  【輸出樣例】

4

【樣例解釋】

  一共有5根廢棄的電話線杆。電話線杆1不能直接與電話線杆4、5相連。電話線杆5不能直接與電話線杆1、3相連。其餘所有電話線杆間均可拉電話線。電信公司可以免費爲FJ連結一對電話線杆。
  FJ選擇如下的連結方案:1->3;3->2;2->5,這3對電話線杆間需要的電話線的長度分別爲4、3、9。FJ讓電信公司提供那條長度爲9的電話線,於是,他所需要購買的電話線的最大長度爲4。

Source  【原題傳送門】

POJ3622 原題傳送門

【思路梳理】

  題偏難,很好綜合了數據結構以及二分猜答案的思想。考慮到每一個電線杆可以視作一個點,每一條電話線可以視爲一條無向帶權邊,這樣轉化爲一道圖論題,使用Dijistra/SPFA;涉及到求最大值最小問題,顯然應該考慮的是二分猜答案思想。
  那麼大致思路就出來了:既然有k條邊我們可以無代價地讓電信局修建,那麼這k條邊就應該是權值最大的前k條邊,把剩下的邊交給FJ自己處理。那麼顯然,將最優路徑構建後,我們的答案就是第k+1長的邊的長度。二分猜答案,猜測的是第k+1條邊的長度(不妨記爲x):如果第i條邊的長度超過了x,那麼這條邊按照我們的思路是應該交給電信局來修建的;否則的話交給FJ處理,不處理這一條邊 (原因很簡單:我們使用的是二分猜答案,猜測第k+1長的邊的長度,這一條邊不一定就是我們要求的第k+1長的那條邊,即使就算是這樣,記錄這條邊也無意義,因爲我們已經在二分猜答案且這不會、一定會是最後的結果)。
  所以不妨將長度大於x的邊的權值記爲1,等於小於x的邊記爲0,看從起點1到終點n的距離dist[n]是否大於k,如果大於k,則說明需要電信局修建多於k條邊(這k條邊的長度都大於我們猜測的值x),猜測值不成立,需要在原區間[s,d]中更大的半個區間[x+1,d];如果猜測值成立,檢查是否能夠讓邊的總長度更小(即讓電信局修的邊的數量更多),我們應該在更小的半個區間[s,x-1]猜測。
  考慮什麼時候輸出-1?從1不能達到n點的時候。那麼我們的dist[n]就會一直等於inf,即無論讓電信局修的邊長度多大、修的邊的數量多少都不能夠使得我們的結點n符合要求。

  下面給出代碼,親測能通過所有數據,10組數據108ms。

【Cpp代碼】
#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
#include<algorithm>
#define maxn 1005
#define maxp 10005
#define inf 0x3fffffff //無窮大的另一種寫法,大家可以學來提升*嗶*(屏蔽音)格 
using namespace std;
int n,p,k,dist[maxn],qn[maxn];
vector<int>g[maxn],w[maxn];//圖的存儲結構不需要贅述
bool inq[maxn];

void SPFA(int t)//SPFA/Dijstra都可
{
    for(int i=1;i<=n;i++)   dist[i]=inf;//dist[i]的含義:在當前猜測值下從起點1連接到結點i所需要的由電信局修建的邊的數量 
    memset(inq,false,sizeof(inq));
    memset(qn,0,sizeof(qn));
    queue<int>q;
    q.push(1);
    inq[1]=true;
    dist[1]=0;
    while(!q.empty())
    {
        int i=q.front();q.pop();inq[i]=false;
        for(int j=0;j<g[i].size();j++)
        {
            int k=g[i][j],c=w[i][j]>t ? 1:0 ;
            //如果這條邊的長度大於了我們的猜測值,那麼就應該交給電信局 
            if(dist[k]>dist[i]+c)
            {
                dist[k]=dist[i]+c;
                if(!inq[k])
                {
                    inq[k]=true;
                    qn[k]++;
                    if(qn[k]>n)     return ;//負權迴路應該是不存在吧? 
                    q.push(k);
                }
            }
        }
    }
}

bool check(long long x)//二分猜答案,保險起見還是用了long long(因爲d是inf,如果s也很大的話兩者一加會超過int) 
{
    SPFA(x);
    if(dist[n]<=k)  return true;//注意一點,可以在這裏判斷是否能夠從結點1找到一條路徑到達結點n 
    return false;//如果不存在這樣的一條路徑,那麼dist[n]顯然等於inf 
}

void solve()
{
    int s=0,d=inf,ans=-1;//ans初值等於-1,即在目前猜測的int範圍內無論猜測值多大都不能夠使得dist[n]小於k,那麼顯然dist[n]=inf 
    while(s<=d)
    {
        long long mid=(long long)s+d>>1;//除以2的另一種寫法,位運算 
        if(check(mid))  ans=mid,d=mid-1;//mid成立,看mid能否更小讓電信局修更多邊 
        else s=mid+1;
    }
    printf("%d",ans);
}

int main()
{
    freopen("in.txt","r",stdin);
    scanf("%d%d%d",&n,&p,&k);
    for(int i=1;i<=p;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        g[x].push_back(y);w[x].push_back(z);
        g[y].push_back(x);w[y].push_back(z);//建立存儲結構 
    }
    solve();


    return 0;
}
發佈了59 篇原創文章 · 獲贊 10 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章