P1948 [USACO08JAN]电话线Telephone Lines
知识点:二分答案,01最短路
原题面
题目要求:
给定 一 无向带权图 ,
可将 图中 \(k\) 条边 边权置零
要使: 从 \(1 \Rightarrow n\) 路径上 最长的边权值最小
求 最小的 路径上的 最长的边权值.
分析题意:
-
求 最小的 路径上的 最长的边权值.
显然 , 答案满足单调性,
可以对 最长的边权值进行 二分答案枚举 -
若已枚举出 最长的边权值 .
如何检查 枚举量的合法性?-
对于 小于等于 枚举量的 边权 , 不影响 最长边权
则 它们 对答案正确性的判断 无贡献. -
对于 大于 枚举量的 边权
如果 必须要被经过 , 会影响 最长的边权
则必须 消耗一次机会 将 其边权值置零
显然 , 若最后 使用边权置零的机会 \(> k\) ,
则证明 这种 连接方案 不合法 -
-
则需统计从 \(1\Rightarrow n\) 路径上 ,
比枚举量大的边 的 最少的个数则可 将 大于枚举量的 边权值看做 \(1\) , 其他看做 \(0\) ;
进行最短路算法 , 求得的\(1\Rightarrow n\) 的 "最短距离"
即 \(1\Rightarrow n\) 路径上 大于枚举量的边 的 最少的个数通过 将求得的最少个数 与 \(k\) 进行比较, 则可检查枚举量的合法性
其他解法:
由于此题 \(n, k\) 较小,
也可以考虑建立分层图 ,
并进行不同层\(1\Rightarrow n\) 最短路 算法
#include<cstdio>
#include<cstring>
#include<queue>
#include<ctype.h>
#define int long long
const int MARX = 1e3+10;
//=============================================================
struct edge
{
int u,v,w,ne;
}e[20*MARX];
int n,p,k,num,ans , head[MARX],dis[MARX];
bool vis[MARX];
//=============================================================
inline int read()
{
int s=1, w=0; char ch=getchar();
for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1;
for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0';
return s*w;
}
void add(int u,int v,int w)
{
e[++num].u = u,e[num].v = v, e[num].w = w;
e[num].ne = head[u], head[u] = num;
}
bool check(int maxx)//检查 最大边权为maxx时的合法性
{
std::priority_queue <std::pair<int,int> > q;//dij最短路预处理
memset(dis,63,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[1] = 0 ;//以1为起点
q.push(std::make_pair(0,1));
for(; !q.empty(); )//dij 最短路
{
std::pair <int,int> top = q.top(); q.pop();
if(vis[top.second]) continue;
vis[top.second] = 1;
for(int i=head[top.second]; i; i = e[i].ne)
if(dis[e[i].v] > dis[e[i].u] + (e[i].w>maxx))
{
//大于枚举量的 边权值看做1, 其他看做0;
dis[e[i].v] = dis[e[i].u] + (e[i].w>maxx);
q.push(std::make_pair(-dis[e[i].v], e[i].v));
}
}
return dis[n] <= k;//检查合法性
}
//=============================================================
signed main()
{
n = read(), p = read(), k = read();
for(int i=1; i<=p; i++)
{
int u = read(), v = read(), w = read();
add(u,v,w), add(v,u,w);
}
if(check(0)) {printf("0"); return 0;}
for(int l=1,r=1e6+10; l<=r;)//二分答案
{
int mid = (l+r) >> 1;
if(check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
printf("%lld",ans==0?-1:ans);
}
作者@Luckyblock,转载请声明出处。