【图论+二分答案】【二分dij】340. 通信线路
题意:
设置一个关于边权的阈值,选择一条从起点到终点的路径,这条路径上的每一条边都有一个边权,这条路径可以最多选择k条路径来进行关闭边权的操作,使得这些边权最终没有统计到路径的和中,从而求一个这样子最小的和。
从贪心的角度:
首先要选择不重复走的路径。
其次k条路要选择这条路径上前k大的路径。也就是实际花费变成了第k+1大的路。也就是要去找到一条路径,且这条路径在所有可行的路径中的第k+1边边权最小。
而不妨设第k+1大边的边权为w,那么前k条边的权值是都是大于w,也就是整条路径上大于w的条数为k。
所以我们不妨可以通过二分答案w,同时以w重新定义边权,如果边权大于w,则边权设置为1,否则设置成0,然后去跑关于新生成的图的最短路。
思路分析:
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f;
#define PII pair<int,int>
#define mp(x,y) make_pair(x,y)
#define FI first
#define SE second
using namespace std;
const int N = 1E3+100, M = 2E4+100;
struct edge{
int u,v,w,nxt;
}edges[M];
int idx,h[N];
void add(int u,int v,int w)
{
edges[idx] = {u,v,w,h[u]};
h[u] = idx++;
}
int dist[N],used[N],n,p,k;
bool dij(int jud)
{
memset(dist,0x3f,sizeof(dist));
memset(used,0,sizeof(used));
priority_queue<PII,vector<PII>,greater<PII>> pq;
dist[1] = 0;
pq.push({0,1});
while(pq.size())
{
PII t = pq.top();
pq.pop();
int d = t.FI,u = t.SE;
if(d>dist[u]||used[u]) continue;
used[u] = true;
for(int i = h[u];~i;i=edges[i].nxt)
{
int v = edges[i].v;
if(dist[v]>d+(edges[i].w>jud?1:0))
{
dist[v] = d + (edges[i].w>jud?1:0);
pq.push({dist[v],v});
}
}
}
return dist[n]<=k;
}
int main()
{
memset(h,-1,sizeof(h));
scanf("%d%d%d",&n,&p,&k);
for(int i=1;i<=p;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
int l = 0 ,r = 1e6+50,ans;
while(l<=r)
{
int mid = l + r >>1;
//cout<<l<<" "<<r<<" "<<mid<<endl;
if(dij(mid))
{
ans = mid;
r = mid-1;
}
else l = mid+1;
}
if(r!=1e6+50)
cout<<ans;
else cout<<-1;
return 0;
}