一道有意思最短路

探讨了两个动点在图上同步移动的问题,要求两点不能同时停留在同一位置,并最终达到各自的目标点。通过定义状态空间和使用广度优先搜索算法(BFS),解决了这一复杂的最短路径问题。

http://codeforces.com/contest/29/problem/E

俩个动点的最短路问题,要求俩个点同时移动且不能同时到达在同一点,俩个点要一直移动不能停留等待,求同时到达各自目标点的最短路径。

定义状态空间[a][b][c](1<=a<=n,1<=b<=n,0<=c<=1)表示动点0在a,动点1在b,当前轮到动点c移动。搜索初始状态为[1][n][0],终止状态为[n][1][0],bfs即可。

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <stack>
#include <queue>
#include <climits>

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

const int MAXN(501);
const int MAXE(20010);
const int INFI((INT_MAX-1)/2);
const ULL LIM(1000000000000000000ull);
template<typename T>
bool checkmin(T &a, const T &b){
    return b < a? (a = b, true): false;
}

template<typename T>
bool checkmax(T &a, const T &b){
    return b > a? (a = b, true): false;
}

inline int lowb(int i){return i&-i;}
int gcd(int a, int b){
    while(b){
        int t = a%b;
        a = b;
        b = t;
    }
    return a;
}

bool vi[MAXN][MAXN][2];
int pre[MAXN][MAXN][2];
struct Q{
    int a, b, c, d;
    Q(){}
    Q(int a_, int b_, int c_, int d_): a(a_), b(b_), c(c_), d(d_){}
} q[MAXN*MAXN*2];
int fr, ba;
// Graph
struct E{
    int v;
    E *next;
};
struct G{
    E e[MAXE], *r;
    E *h[MAXN];
    void init(int n){
        memset(h, 0, sizeof(h[0])*(n+1));
        r = e;
    }
    void add(int u, int v){
        r->v = v;
        r->next = h[u];
        h[u] = r++;
    }
    int bfs(int a, int b){
        for(int i = 1; i <= b; ++i)
            for(int j = 1; j <= b; ++j)
                vi[i][j][0] = vi[i][j][1] = false;
        q[0] = Q(a, b, 0, 0);
        vi[a][b][0] = true;
        while(fr <= ba){
            Q t = q[fr++];
            if(t.a == b && t.b == a && t.c == 0)
                return t.d;
            if(t.c){
                for(E *i = h[t.b]; i; i = i->next)
                    if(!vi[t.a][i->v][0] && i->v != t.a){
                        q[++ba] = Q(t.a, i->v, 0, t.d+1);
                        vi[t.a][i->v][0] = true;
                        pre[t.a][i->v][0] = t.b;
                    }
            }else{
                for(E *i = h[t.a]; i; i = i->next)
                    if(!vi[i->v][t.b][1]){
                        q[++ba] = Q(i->v, t.b, 1, t.d+1);
                        vi[i->v][t.b][1] = true;
                        pre[i->v][t.b][1] = t.a;
                    }
            }
        }
        return -1;
    }
} g;

int p1[MAXN], p2[MAXN];
int main(){
    int n, m, u, v;
    scanf("%d%d", &n, &m);
    g.init(n);
    for(int i = 0; i < m; ++i){
        scanf("%d%d", &u, &v);
        g.add(u, v);
        g.add(v, u);
    }
    int ans = g.bfs(1, n);
    if(ans != -1){
        printf("%d\n", ans/2);
        p1[ans/2] = n;
        p2[ans/2] = 1;
        int i1 = ans/2-1, i2 = ans/2-1;
        int a = n, b = 1, c = 0;
        for(int i = 0; i < ans; ++i){
            if(c){
                a = pre[a][b][c];
                p1[i1--] = a;
            }else{
                b = pre[a][b][c];
                p2[i2--] = b;
            }
            c ^= 1;
        }
        ans /= 2;
        for(int i = 0; i <= ans; ++i){
            if(i) printf(" ");
            printf("%d", p1[i]);
        }
        printf("\n");
        for(int i = 0; i <= ans; ++i){
            if(i) printf(" ");
            printf("%d", p2[i]);
        }
        printf("\n");
    }else
        printf("-1\n");
    return 0;
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值