1. 程式人生 > 其它 >2021牛客寒假演算法基礎集訓營2 E-牛牛與蹺蹺板(尺取+bfs)

2021牛客寒假演算法基礎集訓營2 E-牛牛與蹺蹺板(尺取+bfs)

技術標籤:思維尺取法

題目連結:點這裡~

題目大意

二維平面上有n塊蹺蹺板,每塊蹺蹺板都有三個值:縱座標y_i,橫座標左右端點l_i,r_i,牛牛可以從一塊蹺蹺板走到另一塊蹺蹺板當且僅當兩塊蹺蹺板有公共邊(也就是公共點不算),然後牛牛想從第1塊蹺蹺板走到第n塊蹺蹺板,問走的最少步數。

思路

問最短路徑可以直接bfs,但就是建邊較麻煩。首先建1e5個vector,第i個vector存縱座標為i的蹺蹺板,每個蹺蹺板有三個屬性:編號和左右端點。同一層只需看左邊蹺蹺板的右端點r_i跟右邊蹺蹺板左端點l_{i+1}是否想等即可建邊,否則就看第i層跟第i+1層,這部分用尺取法解決,具體看程式碼。

ac程式碼

#include<bits/stdc++.h>
using namespace std;
#define io cin.tie(0);ios::sync_with_stdio(false);
#define debug(x) cout<<#x<<"="<<x<<endl
#define lowbit(x) x&(-x)
#define pii pair<int,int>
#define mk make_pair
#define ll long long
#define rs p<<1|1
#define ls p<<1
const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
inline ll read(){
    ll p=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){p=(p<<1)+(p<<3)+(c^48),c=getchar();}
    return f*p;
}

struct node{
    int id, l, r; // 編號 左右端點
    bool operator < (const node &a) const{ //按左端點排序
        return l < a.l;
    }
};
vector<node> v[maxn];
vector<int> edge[maxn];
void add(int a, int b){ //建雙向邊
    edge[a].push_back(b);
    edge[b].push_back(a);
}

int dep[maxn];
void bfs(int n){ //跑最短路徑
    fill(dep, dep + maxn, inf);
    dep[1] = 0;
    queue<int> q;
    q.push(1);
    while(q.size()){
        int t = q.front(); q.pop();
        if(t == n) break;
        for(auto it : edge[t]){
            if(dep[t] + 1 < dep[it]){
                dep[it] = dep[t] + 1;
                q.push(it);
            }
        }
    }
}

void solve(){
    int n; cin >> n;
    for(int i = 1; i <= n; i ++){
        int y, l, r;
        cin >> y >> l >> r;
        v[y].push_back({i, l, r});
    }
    for(int i = 0; i < maxn; i ++) sort(v[i].begin(), v[i].end());
    for(int i = 0; i < maxn; i ++){
        if(v[i].size() == 0) continue; 
        int r = 0;
        for(int j = 0; j < v[i].size(); j ++){
            if(j + 1 < v[i].size() && v[i][j].r == v[i][j + 1].l){ //同一層的建邊
                add(v[i][j].id, v[i][j + 1].id);
            }
            if(i + 1 == maxn || v[i + 1].size() == 0) continue; //下一層為空就不需要尺取建邊
            while(r < v[i + 1].size() && v[i + 1][r].r <= v[i][j].l) r ++; //首先他在左邊的全部跑完
            while(r < v[i + 1].size() && v[i + 1][r].l < v[i][j].r) { //有公共邊的建邊
                add(v[i + 1][r].id, v[i][j].id);
                r ++;
            }
            r --; //這裡要減減,因為最後一個跟上一層i有公共邊的,跟上一層i+1也有可能有公共邊
        }
    }
    bfs(n);
    cout << dep[n] << endl;
}
int main(){
    io;
    solve();
    return 0;
}