Gym-100851F Froggy Ford 最短路变形 dijkstra || spfa

题意:有条宽为w的河流,两岸分别在x = 0, x = w处,河中间有n个石头。在河的左岸有一只青蛙想通过石头跳到对岸去。现在可以在河中间某个位置多加一块石头,使得青蛙在单步跳跃中的最大值最小,问应在哪里加。

思路:将左岸抽象成原点,右岸抽象成目标点,原点到任意石头(x, y)的距离都是x,目标点到任意石头的距离都是w - x,任意两石头之间的距离就是他们坐标的欧氏距离,按此建图,跑一个dijkstra,维护最小的最大跳跃距离,dis[u][0]代表没加过石头的答案,dis[u][1]代表加过石头的答案,转移的时候考虑全面一点就好了(详见代码)。

最后计算答案的时候还要注意特判左岸和右岸的情况。

也可以跑spfa,不过因为这是个稠密图,不推荐spfa,硬要写的话最好加上SLF优化。详见:点击打开链接

代码:

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define showtime printf("time = %.15f\n",clock() / (double)CLOCKS_PER_SEC);
using namespace std;
typedef pair<double,double> P;
const int MAXN = 1010;
const double eps = 1e-8;
P p[MAXN];
struct edge{
	int v;
	double w;
	edge(int _v = 0, double _w  = 0) : v(_v), w(_w) {}
};
vector<edge> mp[MAXN];
void addedge(int u, int v, double w)
{
	mp[u].push_back(edge(v, w));
	mp[v].push_back(edge(u, w));
}
double get_dis(P a, P b)
{
	return sqrt((a.first - b.first) * (a.first - b.first) + (a.second - b.second) * (a.second - b.second));
}

struct node{
	int v, l, r;//l, r 表示石头加在l,r线段的中点位置
	bool flag; // 是否已经添加过点 
	double dis;
	node(){}
	node(int _v, int _l, int _r, bool _fg, double _dis) : v(_v), l(_l), r(_r), flag(_fg), dis(_dis) {}
	bool operator < (node a) const{
		//if(fabs(dis - a.dis) < eps) return flag > a.flag;
		return dis > a.dis;
	}
};
int W, n;
priority_queue<node> q;
double dis[MAXN][2];
void dijkstra()
{
	int v, ansl, ansr; double w, tmp, ans_dis = inf;
	for(int i = 0; i <= n + 1; i++) dis[i][0] = dis[i][1] = inf;
	q.push(node(0, 0, 0, 0, 0));
	dis[0][0] = 0;
	while(!q.empty())
	{
		node u = q.top(); q.pop();
		if(dis[u.v][u.flag] < u.dis) continue;
		if(u.v == n + 1 && ans_dis > u.dis)
		ans_dis = u.dis, ansl = u.l, ansr = u.r;
		for(int i = 0; i < mp[u.v].size(); i++)
		{
			v = mp[u.v][i].v; w = mp[u.v][i].w;
			if(u.flag)
			{
				tmp = max(w, u.dis);
				if(dis[v][1] > tmp)
				{
					dis[v][1] = tmp;
					q.push(node(v, u.l, u.r, 1, tmp));
				}
			}
			else
			{
				tmp = max(u.dis, w);
				if(dis[v][0] > tmp)
				{
					dis[v][0] = tmp;
					q.push(node(v, 0, n + 1, 0, tmp));
				}
				tmp = max(u.dis, w * 1.0 / 2);
				if(!u.flag && dis[v][1] > tmp)
				{
					dis[v][1] = tmp;
					q.push(node(v, u.v, v, 1, tmp));
				}
			}
		}
	}
	if(ansl > ansr) swap(ansl, ansr);
	p[0].first = 0; p[n + 1].first = W;
	if(ansl == 0) p[ansl].second = p[ansr].second;
	if(ansr == n + 1) p[ansr].second = p[ansl].second;
	printf("%.1lf %.1lf", (p[ansl].first + p[ansr].first) * 1.0 / 2, (p[ansl].second + p[ansr].second) * 1.0 / 2);
}
int main()
{
	freopen("froggy.in", "r", stdin);
	freopen("froggy.out", "w", stdout);
	cin >> W >> n;
	addedge(0, n + 1, W);
	for(int i = 1; i <= n; i++)
	{
		scanf("%lf %lf", &p[i].first, &p[i].second);
		addedge(0, i, p[i].first);
		addedge(i, n + 1, W - p[i].first);
		for(int j = 1; j < i; j++)
		addedge(i, j, get_dis(p[i], p[j]));
	}
	dijkstra();
 	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值