Cow and Fields(最短路,贪心)

Linking


题意:

给出一个 n 个点 m 条边的无向图,起点为 1,终点为 n。
其中有 k 个特殊的点,现在要加一条边连接两个特殊的点,使得从 1 到 n 的最短路最长。
输出这个最短路长度。

思路:

容易想到其朴素做法,两重循环找出两个点 ,使得起点到一点的最短距离 + 终点到另一点的最短距离 + 1 最大。因为是最短路,所以这个最大值要和原来1到n的最短距离取min

假设找出的两个点为 i 和 j,起点到两个点的距离为xi, xj,终点到两个点的距离为yi, yj,那么加这一条边之后的最短距离为: m i n ( x i , x j ) + m i n ( y i , y j ) + 1 min(x_i, x_j) + min(y_i, y_j) + 1 min(xi,xj)+min(yi,yj)+1.
假设 x i + y j ≤ x j + y i x_i + y_j ≤ x_j + y_i xi+yjxj+yi,那么最短距离就为 x i + y j + 1 x_i+y_j+1 xi+yj+1。后面这个式子是比较简洁的。
为了后面式子成立,要满足前面的式子,也就是要满足 x i − y i ≤ x j − y j x_i-y_i ≤ x_j-y_j xiyixjyj

所以我们可以按照 x i − y i x_i - y_i xiyi 的大小对 pair(xi,yi) 从小到大排序,那么对于 i < j,总是满足 x i + y j ≤ x j + y i x_i + y_j ≤ x_j + y_i xi+yjxj+yi,那么就可以用前面位置的 x i x_i xi 加上后面位置的 y j y_j yj 一直取最值。
这样就可以从后往前走记录 y j y_j yj 的最值往前走用当前 x i x_i xi 更新最大值,O(n)。

Code:

map<int,int> mp;

const int N = 500010, mod = 1e9+7;
int T, n, m, k; 
int a[N];
int e[N], ne[N], h[N], idx;
int stdis[N], endis[N], f[N];
PII p[N];

void add(int x, int y){
	e[idx]=y, ne[idx]=h[x], h[x]=idx++;
}

void dij(int st, int dist[])
{
	mem(f,0);
	for(int i=1;i<=n;i++) dist[i]=0x3f3f3f3f;
	
	priority_queue<PII, vector<PII>, greater<PII>> que;
	que.push({0, st});
	dist[st]=0;
	
	while(que.size())
	{
		int x=que.top().se;
		que.pop();
		if(f[x]) continue;
		f[x]=1;
		
		for(int i=h[x];i!=-1;i=ne[i])
		{
			int tx=e[i];
			if(dist[tx] > dist[x]+1)
				dist[tx] = dist[x]+1, que.push({dist[tx], tx});
		}
	}
}

bool cmp(PII x, PII y){
	return x.fi-x.se < y.fi-y.se;
}

signed main(){
	Ios;
	cin>>n>>m>>k;
	
	int cnt=0;
	for(int i=1;i<=k;i++){
		int x;cin>>x;
		mp[x]=1;
	}
	
	mem(h,-1);
	while(m--)
	{
		int x, y;cin>>x>>y;
		add(x, y), add(y, x);
	}
	
	dij(1, stdis);
	dij(n, endis);
	
	for(int i=1;i<=n;i++){
		if(!mp[i]) continue;
		p[++cnt]={stdis[i], endis[i]};
	}
	
	sort(p+1, p+cnt+1, cmp);
	
	int maxa=0, ans=0;
	for(int i=cnt;i>=1;i--)
	{
		if(i!=cnt) ans = max(ans, p[i].fi + maxa);
		maxa = max(maxa, p[i].se);
	}
	cout<<min(ans+1, stdis[n]);
	
	return 0;
}

经验:

最后用到了一个贪心的排序,排过序之后就可以使原来较为复杂的式子简单化。
很好的一道题!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值