虫洞
此题看似很裸,离散出有用的点,在点和点之间建边跑最短路
但这是错的!
观察可以发现:
- 对于 x — > y x —> y x—>y 的边,x 肯定不能是虫洞起点,因为在起点就会被吸入进虫洞传到终点;
- 除此之外 y 是起点或终点不会影响,但 x — > y x—>y x—>y 的过程中,我们想尽可能跑时间短,肯定每秒跑 S 步,如果跑着的过程中某时刻刚好触碰到一个虫洞的起点,我们肯定会被传送走,直接算的 x — > y x—>y x—>y 的距离是不正确的
- 如何正确计算出保证在不被传送走的情况下的两点间距离?
- 数据较小,所以这里用了一个递归处理:
int getmi(int x, int y) {
if (x == y)return 0;//递归边界,两点重合返回0
if (isbegin(x)) return INF;//若x是起点,这个距离肯定不能计算
int yy = y;
//be从小到大排序
for (int i = 0; i < be.size(); i++)//在存起来的所有是起点的点中
if (be[i] > x && be[i] < yy && (be[i] - x) % S == 0) {
yy = be[i];//找到第一个在范围内同时 S 步走的过程中会误入的虫洞
break;
}
while (yy != y && isbegin(yy))yy--;//此处是起点同时不是终点就往后退一步(不踩到虫洞)(终点是虫洞没关系)
if (yy == x)return INF;//若退回起点证明无路可走了
return ceil((double)(yy - x) / S) + getmi(yy, y);
//否则从 x走到yy(上取整),然后再递归计算 yy到y 这段的距离
}
C o d e Code Code:
#include<bits/stdc++.h>
#include<unordered_set>
#include<unordered_map>
#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define ul (u << 1)
#define ur (u << 1 | 1)
//#define x first
//#define y second
//#pragma GCC optimize(2)
//[博客地址](https://blog.csdn.net/weixin_51797626?t=1)
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
typedef pair <ll, PII> PI;
const int N = 110, M = 200010, MM = N;
int INF = 0x3f3f3f3f, mod = 100003;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, T, S, D;
int g[N][N];
map<int, int> mp;
vector<int> be, d;
vector<PII> pa;
bool isbegin(int x) { //判断是否为起点
auto t = lower_bound(be.begin(), be.end(), x);
if (t == be.end())return false;
return *t == x;
}
int find(int x) { //找虫洞对
return lower_bound(d.begin(), d.end(), x) - d.begin();
}
int getmi(int x, int y) { //递归求距离(细节很多)
if (x == y)return 0;
if (isbegin(x)) return INF;
int yy = y;
for (int i = 0; i < be.size(); i++)
if (be[i] > x && be[i] < yy && (be[i] - x) % S == 0) {
yy = be[i];
break;
}
while (yy != y && isbegin(yy))yy--;
if (yy == x)return INF;
return ceil((double)(yy - x) / S) + getmi(yy, y);
}
void floyd() { //数据小,跑floyd都行
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
}
int main() {
cinios;
while (cin >> D, D) {
cin >> S >> k;
mem(g, 0x3f);
mp.clear();//多组重置
be.clear(), d.clear(), pa.clear();
n = 0;
//map用来离散去重
mp[0] = ++n, mp[D] = ++n;//起点终点记录
for (int i = 1; i <= k; i++) {
int a, b;
cin >> a >> b;
if (!mp[a])mp[a] = ++n;
if (!mp[b])mp[b] = ++n;
pa.push_back({ a,b });//记录虫洞对
be.push_back(a);//记录起点
}
sort(be.begin(), be.end());//起点从小到大排序
int t = 0;
for (auto j : mp)d.push_back(j.first);//取出离散点
sort(d.begin(), d.end());//再从小到大排序
for (auto j : pa)//排过序的点中找到虫洞对,虫洞不花费时间
g[find(j.first)][find(j.second)] = 0;
for (int i = 0; i < n; i++)//从小到大的点枚举两两连边
for (int j = i + 1; j < n; j++)
g[i][j] = min(g[i][j], getmi(d[i], d[j]));
floyd();
cout << g[0][n - 1] << '\n';
}
return 0;
}
/*
*/