【洛谷 P2226】 [HNOI2001]遙控賽車比賽(最短路)

題目鏈接
首先拆點,把每個點拆成4個點,表示到達這個點的時候賽車的朝向。
然後考慮連邊。
相鄰同向並且都是可以走的點直接連邊權1的邊。
至於怎麼轉向,只需在每個點\(i\)向每個方向一直拓展直到不能走爲止,如果當前點的深度大於靈敏度,從\(i\)向這個點的其它3個方向都連一條邊權爲這個點的深度的邊。
然後跑\(SPFA\)(至於爲什麼是\(SPFA\)我纔不會告訴你是博主懶),這樣你可以獲得\(90pts\)
爲什麼?因爲我們最多要跑\(10\)次,而每次連邊的時間複雜度都是\(O(n^3)\)級別的,加上要跑\(SPFA\),很容易超時。
所以考慮一個優化,顯然每次連邊有很多重複的地方,因爲靈敏度爲\(2\)的情況肯定也把靈敏度爲\(3\)的情況的所有邊都連了。
所以我們不妨反過來跑,靈敏度從\(10\)\(1\),每次只連長度剛好爲靈敏度的轉向邊,用棧記錄一下答案就行了。

\(90pts\)

#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#define Open(s) freopen(s".in","r",stdin); freopen(s".out","w",stdout);
#define Close fclose(stdin); fclose(stdout);
#define O(x) cout << #x << "=" << x << endl;
using namespace std;
const int MAXN = 10010 << 2;
const int MAXM = 5000000;
struct Edge{
    int next, to, dis;
}e[MAXM << 1];
int head[MAXN], num, dis[MAXN], vis[MAXN], a[110][110];
inline void Add(int u, int v, int dis){
    e[++num] = (Edge){ head[u], v, dis }; head[u] = num;
}
int n, m, N, l[] = { -1, 1, 0, 0 }, r[] = { 0, 0, -1, 1 }, sx, sy, px, py;
inline int id(int x, int y, int direct){
    return direct * N + (x - 1) * m + y;
}
queue <int> q;
int work(int check){
    num = 0; memset(head, 0, sizeof head);
    memset(dis, 127, sizeof dis);
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j){
            if(!a[i][j]) continue;
            for(int k = 0; k < 4; ++k){
                int x = i + l[k], y = j + r[k];
                if(!a[x][y]) continue;
                Add(id(i, j, k), id(x, y, k), 1);
                for(int o = 1; a[x][y]; x += l[k], y += r[k], ++o)
                    if(o >= check)
                        for(int l = 0; l < 4; ++l)
                            if(l != k)
                                Add(id(i, j, k), id(x, y, l), o);
            }
        }
    dis[id(sx, sy, 0)] = dis[id(sx, sy, 1)] = dis[id(sx, sy, 2)] = dis[id(sx, sy, 3)] = 0;
    vis[id(sx, sy, 0)] = vis[id(sx, sy, 1)] = vis[id(sx, sy, 2)] = vis[id(sx, sy, 3)] = 1;
    q.push(id(sx, sy, 0)); q.push(id(sx, sy, 1)); q.push(id(sx, sy, 2)); q.push(id(sx, sy, 3));
    while(q.size()){
        int u = q.front(); q.pop();
        vis[u] = 0;
        for(int i = head[u]; i; i = e[i].next)
            if(dis[e[i].to] > dis[u] + e[i].dis){
                dis[e[i].to] = dis[u] + e[i].dis;
                if(!vis[e[i].to]){
                    vis[e[i].to] = 1;
                    q.push(e[i].to);
                }
            }
    }
    int ans = min(min(dis[id(px, py, 0)], dis[id(px, py, 1)]), min(dis[id(px, py, 2)], dis[id(px, py, 3)]));
    if(ans < 10000000) return printf("%d %d\n", check, ans), 0;
    else return 1;
}
int main(){
    scanf("%d%d%d%d%d%d", &n, &m, &sx, &sy, &px, &py); N = n * m;
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j)
            scanf("%d", &a[i][j]);
    for(int i = 1; i <= 10; ++i)
        if(work(i))
            break;
    return 0;
}

\(100pts\)

#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#define Open(s) freopen(s".in","r",stdin); freopen(s".out","w",stdout);
#define Close fclose(stdin); fclose(stdout);
#define O(x) cout << #x << "=" << x << endl;
using namespace std;
const int MAXN = 10010 << 2;
const int MAXM = 5000000;
struct Edge{
    int next, to, dis;
}e[MAXM << 1];
int head[MAXN], num, dis[MAXN], vis[MAXN], a[110][110], s[12], top;
inline void Add(int u, int v, int dis){
    e[++num] = (Edge){ head[u], v, dis }; head[u] = num;
}
int n, m, N, l[] = { -1, 1, 0, 0 }, r[] = { 0, 0, -1, 1 }, sx, sy, px, py;
inline int id(int x, int y, int direct){
    return direct * N + (x - 1) * m + y;
}
queue <int> q;
void work(int check){
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j){
            if(!a[i][j]) continue;
            for(int k = 0; k < 4; ++k){
                for(int p = 1, x = i + l[k], y = j + r[k]; a[x][y]; ++p, x += l[k], y += r[k])
                    if(p == check){
                        for(int l = 0; l < 4; ++l)
                            if(l != k)
                                Add(id(i, j, k), id(x, y, l), check);
                        break;
                    }
            }
        }
    memset(dis, 127, sizeof dis);
    dis[id(sx, sy, 0)] = dis[id(sx, sy, 1)] = dis[id(sx, sy, 2)] = dis[id(sx, sy, 3)] = 0;
    vis[id(sx, sy, 0)] = vis[id(sx, sy, 1)] = vis[id(sx, sy, 2)] = vis[id(sx, sy, 3)] = 1;
    q.push(id(sx, sy, 0)); q.push(id(sx, sy, 1)); q.push(id(sx, sy, 2)); q.push(id(sx, sy, 3));
    while(q.size()){
        int u = q.front(); q.pop();
        vis[u] = 0;
        for(int i = head[u]; i; i = e[i].next)
            if(dis[e[i].to] > dis[u] + e[i].dis){
                dis[e[i].to] = dis[u] + e[i].dis;
                if(!vis[e[i].to]){
                    vis[e[i].to] = 1;
                    q.push(e[i].to);
                }
            }
    }
    int ans = min(min(dis[id(px, py, 0)], dis[id(px, py, 1)]), min(dis[id(px, py, 2)], dis[id(px, py, 3)]));
    if(ans < 10000000) s[++top] = ans;
}
int main(){
    scanf("%d%d%d%d%d%d", &n, &m, &sx, &sy, &px, &py); N = n * m;
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j)
            scanf("%d", &a[i][j]);
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j){
            if(!a[i][j]) continue;
            for(int k = 0; k < 4; ++k){
                int x = i + l[k], y = j + r[k];
                if(!a[x][y]) continue;
                Add(id(i, j, k), id(x, y, k), 1);
            }
        }
    for(int i = 10; i; --i)
        work(i);
    for(int Top = top, i = 1; i <= Top; ++i)
        printf("%d %d\n", i, s[top--]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章