【图论+二分答案】【二分dij】340. 通信线路

【图论+二分答案】【二分dij】340. 通信线路

image

题意:

设置一个关于边权的阈值,选择一条从起点到终点的路径,这条路径上的每一条边都有一个边权,这条路径可以最多选择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;
}
原文地址:https://www.cnblogs.com/BeautifulWater/p/15557878.html