题意
给你n个城市,编号为0到n-1,然后每个城市有一只狗,拥有pi的弹跳力,也就是可以走到相邻的x-pi和x+pi,当然你可以走多次
现在你可以选择当前城市的一条狗来跳,中途可以换狗,目标是你从第0只狗跳到第1只狗的最少操作
1<=n<=30000
1<=pi<=30000
2<=m<=30000
分析
一个显而易见的小结论:每个狗只用一次,并且bfs的时候,不会走到重复的点
首先一种暴力的方法就是你可以对于每个城市预处理一下到别的城市的距离,这个是nm的,然后就是一个n^2条边的最短路,这个方法的时间复杂度是
考虑一下优化,我们可以把边分个组,小于等于200的一组,大于的一组,然后小于的话我们可以记录一个vis[x][y]表示x这个点有没有跳跃能力为y的狗,如果当前点x,用跳跃能力为y的狗,跳到一个城市p,同时这个城市也有跳跃能力为y的狗,也就是说vis[p][y] = true,那么就可以停下来
大于200的暴力跳,总的时间复杂度是
代码
#include <bits/stdc++.h>
#define pb push_back
#define MP make_pair
using namespace std;
const int N = 30010;
const int inf = 1e9;
inline int read()
{
char ch=getchar(); int p=0; int f=1;
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
vector<int>g[N]; int d[N]; int s,t,n,m;
bool vis[N][202]; queue<int> q; bool v[N];
void bfs()
{
memset(d,63,sizeof(d)); d[s] = 0;
memset(v,0,sizeof(v)); v[s] = 1;
while(!q.empty()) q.pop(); q.push(s);
while(!q.empty())
{
int x = q.front(); v[x] = 0; q.pop();
for(int i=0;i<g[x].size();i++)
{
int delta = g[x][i]; int ss = 0;
for(int j=x+delta;j<n;j+=delta)
{
ss++; if(d[j] > d[x] + ss)
{
d[j] = d[x] + ss;
if(!v[j]){v[j] = 1; q.push(j);}
}
if(delta <= 200 && vis[j][delta]) break;
}
ss=0; for(int j=x-delta;j>=0;j-=delta)
{
ss++; if(d[j] > d[x] + ss)
{
d[j] = d[x] + ss;
if(!v[j]){v[j] = 1; q.push(j);}
}
if(delta <= 200 && vis[j][delta]) break;
}
}
}
if(d[t] < inf) printf("%d\n",d[t]);
else printf("-1\n");
}
int main()
{
n = read(); m = read();
for(int i=1;i<=m;i++)
{
int x = read(); int y = read();
g[x].pb(y);
if(i==1) s=x;
if(i==2) t=x;
if(y<=200) vis[x][y] = 1;
}
for(int i=1;i<=n;i++)
{
sort(g[i].begin(),g[i].end());
int len = unique(g[i].begin(),g[i].end()) - g[i].begin();
g[i].resize(len);
}
bfs();
return 0;
}