1. 程式人生 > 其它 >【P1948 [USACO08JAN]Telephone Lines S】題解

【P1948 [USACO08JAN]Telephone Lines S】題解

題目連結

題目

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.

多年以後,笨笨長大了,成為了電話線佈置師。由於地震使得某市的電話線全部損壞,笨笨是負責接到震中市的負責人。該市周圍分佈著 \(1\le N\le1000\) 根據 \(1\cdots N\) 順序編號的廢棄的電話線杆,任意兩根線杆之間沒有電話線連線,一共有 \(1\le p\le10000\) 對電話杆可以拉電話線。其他的由於地震使得無法連線。

第i對電線杆的兩個端點分別是 \(a_i\)\(b_i\),它們的距離為 \(1\le l_i\le1000000\)。資料中每對 \((a_i,b_i)\) 只出現一次。編號為 \(1\) 的電話杆已經接入了全國的電話網路,整個市的電話線全都連到了編號 \(N\) 的電話線杆上。也就是說,笨笨的任務僅僅是找一條將 \(1\) 號和 \(N\) 號電線杆連起來的路徑,其餘的電話杆並不一定要連入電話網路。

電信公司決定支援災區免費為此市連線 \(k\) 對由笨笨指定的電話線杆,對於此外的那些電話線,需要為它們付費,總費用決定於其中最長的電話線的長度(每根電話線僅連線一對電話線杆)。如果需要連線的電話線杆不超過 \(k\) 對,那麼支出為 \(0\)

請你計算一下,將電話線引導震中市最少需要在電話線上花多少錢?

思路

最大的最小,顯然是二分

二分\(K+1\)值,然後最短路求一遍最少需要免費多少條電話線杆。

在最短路的過程中,大於當前二分的值邊權為1,小於則為0。

然後判斷最短路跑出的數量和 \(K\) 的關係,大於則可行,小於則不可行

Code

// Problem: P1948 [USACO08JAN]Telephone Lines S
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1948
// Memory Limit: 125 MB
// Time Limit: 1000 ms

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define N 10010
//#define M
//#define mo
struct node
{
	int x, y, z, n; 
}d[N<<1]; 
int n, m, i, j, k, T; 
int c, u, v, w, h[N], ans[N], b[N]; 
int l, r, mid, g; 
queue<int>q; 

void cun(int x, int y, int z)
{
	d[++k].x=x; d[k].y=y; d[k].z=z; 
	d[k].n=h[x]; h[x]=k; 
}

int check(int x)
{
	memset(ans, 0x3f, sizeof(ans)); 
	ans[1]=0; q.push(1); 
	while(!q.empty())
	{
		u=q.front(); q.pop(); 
		b[u]=0; 
		// printf("%lld\n", u); 
		for(g=h[u]; g; g=d[g].n)
		{
			v=d[g].y; w=(d[g].z>x ? 1 : 0); 
			if(ans[u]+w<ans[v])
			{
				ans[v]=ans[u]+w; 
				if(!b[v]) b[v]=1, q.push(v); 
			}
		}
	}
	// printf("%lld %lld\n", x, ans[n]); 
	return (ans[n]<=c ? 1 : 0); 
}

signed main()
{
//	freopen("tiaoshi.in","r",stdin);
//	freopen("tiaoshi.out","w",stdout);
	n=read(); m=read(); c=read(); 
	for(i=1; i<=m; ++i)
	{
		u=read(); v=read(); w=read(); 
		cun(u, v, w); 
		cun(v, u, w); 
	}
	if(!check((int)1e9)) return printf("-1"), 0; 
	l=0; r=1e9; 
	while(l<r)
	{
		mid=(l+r)>>1; 
		if(check(mid))  r=mid; 
		else l=mid+1; 
	}
	printf("%lld", l); 
	return 0;
}

總結

這是一道非常經典的二分題。

首先看到最大的最小顯然可以發現是二分,然後轉化為01邊權問題。

這類思想在很多問題中會用到。