USACO 2008 JAN Telephone Lines 【二分答案、SPFA】

题目

题目描述

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.

【洛谷翻译太愚蠢被我删了~】

输入输出格式

输入格式:

输入文件的第一行包含三个数字n,p,k;

第二行到第p+1行,每行分别都为三个整数ai,bi,li。

输出格式:

一个整数,表示该项工程的最小支出,如果不可能完成则输出-1.

输入输出样例

输入样例#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

输出样例#1:

4 

分析

【瞎扯】

不好意思我又要瞎扯了,还是提醒各位审题!

拿到题目看了一遍然后默默地想起了【BZOJ 2763 飞行路线】(别找了这题我压根儿就没过)。题面完全一毛一样的!然后所以这是分层图?等下我们图论专题才刚开始上了半天就给我做分层图?哦是吗我要投诉清北学堂。

【然后我去向老师吐槽了一下】

老师:

……又没看题目



【正经】

首先我们来一起读题【雾

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.

本题最重要的一句话↑,它告诉我们,电信公司【移动:?】愿意支付其中k条电话线的费用,其余的电话线中,FJ支付最贵的一条(电信公司真差劲,下次找移动【电信:?】)。简单想一下:贪心的想法,最佳情况,我们找前k贵的让电信公司付钱,第k+1贵的让FJ付钱。

假设我们有一个check(int c)来检查当我们支付价格为c的电话线时,我们是否能成功让整个线路运行起来。有了它我们就可以肆无忌惮地二分了!原因很简单:显然是单调的且我们有check()来帮我们检查可行性。

那么本题的终点就转移到了如何写出check()函数。

假如当前我们二分到mid,我们将所有权值大于mid的所有电话线的权值全部设置为1(意味着我们需要消耗一次【电话线抵用券】来假设电话线),而权值小于mid则全部设置为0(我们不需要让FJ付款,也不需要让电信掏钱)。对于这个0/1的图跑SPFA,如果我们发现最短路径长度大于k,意味着这个k然而并不可行,我们缩小左边界继续二分;若我们发现它小于等于k,说明我们仍然有可能有更优解,缩小右边界继续二分。

本题的一大重点还是搞清楚left/mid/right的关系,或者说这就是二分的一个难点吧。

程序

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int MAXN = 2000, MAXM = 20000;
 4 int n, m, k, Head[MAXN], Edge_Count, ans, G[MAXN][MAXN];
 5 struct edge
 6 {
 7     int Next, From, Aim, Weight;
 8 }Edge[MAXM];
 9 void insert(int u, int v, int w)
10 {
11     Edge[++Edge_Count] = (edge){Head[u], u, v, w};
12     Head[u] = Edge_Count;
13 }
14 bool check(int Len)
15 {
16     // 初始化G数组为-1,-1代表没有边相连
17     for (int i = 1; i < MAXN; i++)
18         for (int j = 1; j < MAXN; j++)
19             G[i][j] = -1;
20     memset(dis,0x3F,sizeof(dis));
21     for (int i = 1; i <= Edge_Count; i++)
22         if (Edge[i].Weight > Len)
23             G[Edge[i].From][Edge[i].Aim] = 1, G[Edge[i].Aim][Edge[i].From] = 1;
24         else
25             G[Edge[i].From][Edge[i].Aim] = 0, G[Edge[i].Aim][Edge[i].From] = 0;
26     // 0:有边相连,且权值小于Len
27     // 1:有边相连,且权值大于Len,消耗一次电话线抵用券
28     // 以下标准SPFA
29     queue<int> Q;
30     Q.push(1);
31     int dis[MAXN];
32     dis[1] = 0;
33     bool vis[MAXN];
34     vis[1] = true;
35     while (!Q.empty())
36     {
37         int u = Q.front();
38         Q.pop();
39         for (int i = 1; i <= n; i++)
40         {
41             if (G[u][i] != -1 && dis[u]+G[u][i] < dis[i])
42             {
43                 dis[i] = dis[u]+G[u][i];
44                 if (!vis[i])
45                     vis[i] = true, Q.push(i);
46             }
47         }
48         vis[u] = false;
49     }
50     // 若最少的小号次数大于k,这种情况不可信,返回假
51     // 否则说明是一个可行解,但不一定是最优解,返回真继续二分
52     if (dis[n] > k)
53         return false;
54     else
55         return true;
56 }
57 int main()
58 {
59     freopen("tele.in","r",stdin);
60     cin >> n >> m >> k;
61     for (int i = 1; i <= m; i++)
62     {
63         int u, v, w;
64         cin >> u >> v >> w;
65         insert(u,v,w);
66         insert(v,u,w);
67     }
68     int l = 0, r = 1000000000, mid;
69     bool flag = false;
70     while (l < r)
71     {
72         mid = (l+r)/2;
73         if (check(mid))
74             r = mid, flag = true;
75         else
76             l = mid + 1;
77     }
78     // flag说明我们是否找到过可行的解
79     // 若没有,输出-1
80     if (flag)
81         cout << l << endl;
82     else
83         cout << -1 << endl;
84     return 0;
85 }

原文地址:https://www.cnblogs.com/OIerPrime/p/8443352.html

时间: 2024-10-10 21:29:57

USACO 2008 JAN Telephone Lines 【二分答案、SPFA】的相关文章

bzoj1614[Usaco2007 Jan]Telephone Lines架设电话线*

bzoj1614[Usaco2007 Jan]Telephone Lines架设电话线 题意: n个节点,1号节点已经连入互联网,现在需要将整个图连入网络.有K条边可以免费连接,最后总费用为所有连边费用的最大值,求最小总费用.n≤10000 题解: 二分费用,将连边费用大于二分值的长度记为1,否则记为0,求最短路,如果到某个点的距离超过k,则需要增加答案,继续二分. 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include &l

[BZOJ] 1614: [Usaco2007 Jan]Telephone Lines架设电话线

1614: [Usaco2007 Jan]Telephone Lines架设电话线 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1806  Solved: 773[Submit][Status][Discuss] Description Farmer John打算将电话线引到自己的农场,但电信公司并不打算为他提供免费服务.于是,FJ必须为此向电信公司支付一定的费用. FJ的农场周围分布着N(1 <= N <= 1,000)根按1..N顺次编号的废

【SPFA+二分答案】BZOJ1614- [Usaco2007 Jan]Telephone Lines架设电话线

沉迷于刷水 以前的那个二分写法过不了QAQ 换了一种好像大家都比较常用的二分.原因还不是很清楚. [题目大意] 给出一张图,可以将其中k条边的边权减为0,求1到n的路径中最长边的最小值. [思路] 二分答案,即最长边的最小值x.对于每次check(x),我们将边权大于x的边设为1,边权小于等于x的边设为0,跑SPFA,结果相当于最少经过多少条边权大于x的边.如果SPFA结果>k,则不可行,否则可行. 1 #include<iostream> 2 #include<cstdio>

bzoj1614: [Usaco2007 Jan]Telephone Lines架设电话线(二分答案 + spfa)

原题链接 题目描述:FarmerJohn打算将电话线引到自己的农场,但电信公司并不打算为他提供免费服务.于是,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).数

POJ 3662 Telephone Lines(二分答案+SPFA)

[题目链接] http://poj.org/problem?id=3662 [题目大意] 给出点,给出两点之间连线的长度,有k次免费连线, 要求从起点连到终点,所用的费用为免费连线外的最长的长度. 求最小费用. [题解] 二分答案,对于大于二分答案的边权置为1,小于等于的置为0, 则最短路就是超出二分答案的线数,如果小于等于k,则答案是合法的 [代码] #include <cstdio> #include <cstring> using namespace std; const i

【POJ3657】【USACO 2008 Jan Gold】 1.Haybale Guessing 二分答案,并查集check

题意: 输入n.m表示数列长度为n,有m条有序的限制{l,r,x}. 限制:l~r间所有数最小值为x. 问到第几条限制开始出现矛盾,都不出现输出"0". 题解: 首先这题比较厉害,正常解有点难,不妨转化成二分答案. 我们二分"答案",也就是第ans条出现矛盾. 考虑到若一条限制S所在区间被另一个限制Seg包含,且Seg这条限制的x又比S.x大, 那么也就是意为 ① [Seg.l,Seg.r]间最小值为Seg.x ② [S  .l,S  .r]间最小值为S  .x ③

1614: [Usaco2007 Jan]Telephone Lines架设电话线

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1614 做法:二分答案 首先附上让我找了两天错的代码 #include<queue> #include<iostream> #include<cstdio> #include<cstring> using namespace std; struct data{int to,next,c;}e[20001]; int n,p,k,cnt,head[1001

[Usaco2007 Jan]Telephone Lines架设电话线

好题!...(躺) 一开始推个分层图最短路...然后发现并不是求最短路(躺 这题题目大意我没看错应该是这样的: 找一条1~n的路径,使路径上第k+1大的数尽可能的小 这个提法是不是有点眼熟... 假设有这样一个问题: 找一条1~n的路径,使路径上最大的数最小 “使最大的最小”,二分答案! 然后我就不会了 hzwer有一个很妙的思路orz... 大概是这个意思吧... 二分一下路径上最大的边权mx,然后对最短路变一下形 把>mx的边权当做1,<=mx的边权当做0,因为>mx的肯定要用一次减

【二分答案】【最短路】bzoj1614 [Usaco2007 Jan]Telephone Lines架设电话线

对于二分出的答案x而言,验证答案等价于将所有边权>x的边赋成1,否则赋成0,然后判断从1到n的最短路是否<=K. #include<cstdio> #include<cstring> #include<queue> using namespace std; #define N 1001 #define M 10001 int n,m,K,Xs[M],Ys[M],Zs[M]; int first[N],next[M<<1],v[M<<1