ジャカルタの超高層ビル
質問の説明の意味:
- そこ(N \)\左から右に番号が付けられ、高層ビルを\(0 \)する\(1-N \) 。
- そこ(M \)\情報伝達部材THが順次番号付けされている\(0 \)する\(1-M \) 。私はもともと数に渡されたメンバーの数\(B_i \)番高層ビル、超高層ビルの間に、郵便配達が(前方または後方)をスキップすることができます\(私は\)転写部材跳躍能力のです\(P_Iを\します)。
- 転送乗組員は超高層ビルに到達するとき、彼は2つの操作を行うことができます
- 1:上の階の他の観覧にジャンプ。
- 2:高層ビルの他の電流伝達部材へのメッセージ。
- 最終的には番号が付けするには、\(0 \)番号に超高層ビルの情報を\(1 \)超高層ビルの場所。
入力フォーマット:
- 最初の入力ライン\(N \)と\(M \) 。\((1 \当量のn \の当量3×10 ^ 4,1 \当量のm \の当量3×10 ^ 4)\)
- 次に、第\(2 \)をする(Mは+。1 \)を\、各行は2つの整数を含む\(B_i \)と\(P_I \) 。
出力フォーマット:
- あなたは出力に到達できない場合は、ステップの最小数を表す出力ライン、\を(--1 \) 。
問題解決のアイデア:
- トピックを分析した後と考えることができます離れてジャンプすることができ、あなたは右の1ながら、開始と終了を確立するためにすることができ、各転写部材のための最短経路です。
- 暴力の縁取りの練習を考えてみますが、場合\(のp \)は幼い頃、最悪のケースであり、\(N ^ 2 \)エッジ、任意の最短経路アルゴリズムを渡すことはできません。
- :ブロックの思考を使用して構築された図の最適化\(N- \)点がマージ(\ \のSQRT {N} \ ) ブロック
- 以下のための\(P_I \当量\ SQRT { N} \) 伝達部材。
- 超高層ビルの場合、それは彼が真の超高層ビルをポイントから変換されました。
- 各建物の建設のための\(\のSQRT {N} \ ) 層、各\(N- \)点、第一層が表す\(P = 1 \)と散歩を、第2の層は表し\(pは= 2 \)と散歩、..... どこに最初の\(0 \)元、その点に代わって。
- そして、暴力プラス側。最初の\(Iは\)層を表し(= P iは\)\の場合、ピッチは\(Iは\)相互接続長ポイント\(1 \)側。すべてのこれらの補助点が下端に接続されています。
- それぞれのための\(P_I> \ SQRT {N } \) のすべての点達成可能な点で\(0 \)ボーダー層。
- エッジの総数を超えていない\(N \のSQRT {N}を\) 。
- 建設計画は、最短ランを完了しています。
コード:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = (3e4 + 10)*500;
const int maxm = (3e4 + 10)*500;
int n, m;
int s, t; //起点和终点
int B[maxn], P[maxn]; //起始于B, 跳跃能力为P
int block;
int head[maxn], nex[maxn<<1], ver[maxn<<1], edge[maxn<<1], tot;
inline void add_edge(int x, int y, int z)
{
ver[++tot] = y; edge[tot] = z;
nex[tot] = head[x]; head[x] = tot;
}
inline int get_block(int x, int y){
return x*n + y;
}
int dist[maxn];
bool v[maxn];
void SPFA()
{
memset(dist, 0x3f, sizeof(dist));
dist[s] = 0; queue<int> q;
q.push(s); v[s] = 1;
while(q.size())
{
int x = q.front(); q.pop();
v[x] = 0;
for(int i = head[x]; i; i = nex[i])
{
int y = ver[i], z = edge[i];
if(dist[y] > dist[x] + z)
{
dist[y] = dist[x] + z;
if(!v[y])
{
q.push(y);
v[y] = 1;
}
}
}
}
printf("%d\n", dist[t] == 0x3f3f3f3f ? -1 : dist[t]);
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1, x, y; i <= m; i++)
{
scanf("%d%d", &x, &y);
B[i] = x + 1, P[i] = y;
}
//起始位置 终止位置 块的大小
s = B[1], t = B[2]; block = min((int)sqrt(n), 100);
//每一个点转化为一栋楼, 有sqrt(n)层
for(int i = 1; i <= block; i++)
for(int j = 1; j <= n; j++)
{
add_edge(get_block(i, j), j, 0); //楼底到每一层连边
if(j <= n - i) //j条一次跳出范围了
{
//每一层间隔为i的点 跳一次边权为1
add_edge(get_block(i,j), get_block(i,j)+i, 1);
add_edge(get_block(i,j)+i, get_block(i,j), 1);
}
}
for(int i = 1; i <= m; i++) //考虑每一个doge的跳跃能力
{
// 对于每一个传递员, 从楼底到Pi层对应连边
if(P[i] <= block)
add_edge(B[i], get_block(P[i], B[i]), 0);
else
{ //如果大于一个块了 那就暴力加边 因为有sqrt个块 所以加sqrt条边
for(int j = 1; B[i] + j * P[i] <= n; j++)
add_edge(B[i], B[i] + j*P[i], j); //向前跳
for(int j = 1; B[i] - j * P[i] >= 1; j++)
add_edge(B[i], B[i] - j*P[i], j); //向后跳
} //这里最大可能加N*sqrt(N)条边
} SPFA(); //优化后边数有 N*sqrt(N)的规模
return 0;
}