Uvalive 4128 - Steam Roller(最短路+思维)

题目链接 https://vjudge.net/problem/UVALive-4128

【题意】
有一个r条横线c条竖线组成的网络,你的任务是开着一辆蒸汽式压路机,用最短的时间从(r1,c1)出发,最后到达目的地(r2,c2),其中一些线段上有权值代表全速通过所需要的时间,权值为0的边表示不能通过。由于压路机的惯性较大,对于任意一条边,如果进入这条边之前刚转弯或者离开这条边以后要立即转弯,则实际时间是理想时间的2倍,并且整条路线的起点和终点所在的边也需要两倍时间,“时间加倍”不可叠加。对于每组数据,求出最短时间,无法到达输出”Impossible”

【思路】
大白书336页原题,把某个位置(r,c)拆分成8种不同状态(r,c,dir,doubled)代表上一步从哪个方向(dir)移动到这个点,以及上条边是否已经加倍。然后计算出每种状态的后继状态,建立有向边,最后套用dijkstra算法模板求解,思路不算复杂,但是细节实在特别多,基本上是把书上的代码看会又抄一遍。

#include<bits/stdc++.h>
using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 80500;

struct Edge {
    int from, to, dist;
    Edge(int f, int t, int d) :from(f), to(t), dist(d) {}
};

struct HeapNode {
    int d, u;
    HeapNode(int dd, int uu) :d(dd), u(uu) {}
    bool operator<(const HeapNode& rhs)const {
        return d > rhs.d;
    }
};

struct Dijkstra {
    int n, m;
    vector<Edge> edges;
    vector<int> g[maxn];
    bool done[maxn];
    int d[maxn];
    int p[maxn];

    void init(int n) {
        this->n = n;
        for (int i = 0; i < n; ++i)g[i].clear();
        edges.clear();
    }

    void add(int from, int to, int dist) {
        edges.push_back(Edge(from, to, dist));
        m = edges.size();
        g[from].push_back(m - 1);
    }

    void dijkstra(int s) {
        priority_queue<HeapNode> que;
        for (int i = 0; i < n; ++i) d[i] = inf;
        d[s] = 0;
        memset(done, 0, sizeof(done));
        que.push(HeapNode(0, s));
        while (!que.empty()) {
            HeapNode x = que.top();
            que.pop();
            int u = x.u;
            if (done[u])continue;
            done[u] = true;
            for (int i = 0; i < g[u].size(); ++i) {
                Edge& e = edges[g[u][i]];
                if (d[e.to] > d[u] + e.dist) {
                    d[e.to] = d[u] + e.dist;
                    p[e.to] = g[u][i];
                    que.push(HeapNode(d[e.to], e.to));
                }
            }
        }
    }
};

const int UP = 0, LEFT = 1, DOWN = 2, RIGHT = 3;
const int dr[4] = { -1,0,1,0 };
const int dc[4] = { 0,-1,0,1 };
const int inv[4] = { 2,3,0,1 };//记录反方向的下标

const int maxr = 105;
const int maxc = 105;

int readint() { int x; scanf("%d", &x); return x; }

int n;//结点总数
int grid[maxr][maxc][4];//grid[r][c][dir]记录从(r,c)沿dir方向出发的边权
int id[maxr][maxc][4][2];//id[r][c][dir][double]记录状态为(r,c,dir,double)的结点编号
int R, C, r1, c1, r2, c2;
Dijkstra solver;

int ID(int r, int c, int dir, int doubled) {//给结点编号
    int& x = id[r][c][dir][doubled];
    if (x == 0) x = ++n;//编号从1开始
    return x;
}

bool cango(int r, int c, int dir) {//从(r,c)处是否可以沿着dir方向走
    if (r < 0 || r >= R || c < 0 || c >= C) return false;
    return grid[r][c][dir] > 0;
}

int main() {
    int kase = 0;
    while (scanf("%d%d%d%d%d%d", &R, &C, &r1, &c1, &r2, &c2) == 6 && R) {//输入和初始化
        --r1, --c1, --r2, --c2;
        memset(grid, 0, sizeof(grid));
        for (int i = 0; i < R; ++i) {
            for (int j = 0; j < C - 1; ++j) {
                grid[i][j][RIGHT] = grid[i][j + 1][LEFT] = readint();
            }
            if (i + 1 != R) {
                for (int j = 0; j < C; ++j) {
                    grid[i][j][DOWN] = grid[i + 1][j][UP] = readint();
                }
            }
        }
        solver.init(8 * R*C + 1);//源点编号为0

        n = 0;
        memset(id, 0, sizeof(id));

        //源点出发的边
        for (int dir = 0; dir < 4; ++dir) {
            if (cango(r1, c1, dir)) {
                solver.add(0, ID(r1 + dr[dir], c1 + dc[dir], dir, 1), grid[r1][c1][dir] * 2);
            }
        }

        //计算每个状态的后继状态
        for (int i = 0; i < R; ++i) 
            for (int j = 0; j < C; ++j) 
                for (int dir = 0; dir < 4; ++dir) 
                    if (cango(i, j, inv[dir])) 
                        for (int newdir = 0; newdir < 4; ++newdir) 
                            if (cango(i, j, newdir))
                                for (int doubled = 0; doubled < 2; ++doubled) {
                                    int newr = i + dr[newdir];
                                    int newc = j + dc[newdir];
                                    int v = grid[i][j][newdir], newdoubled = 0;
                                    if (dir != newdir) {
                                        if (0 == doubled) { v += grid[i][j][inv[dir]]; }
                                        newdoubled = 1;
                                        v += grid[i][j][newdir];
                                    }
                                    solver.add(ID(i, j, dir, doubled), ID(newr, newc, newdir, newdoubled), v);
                                }

        solver.dijkstra(0);

        int ans = inf;
        for (int dir = 0; dir < 4; ++dir) {
            for (int doubled = 0; doubled < 2; ++doubled) {
                int v = solver.d[ID(r2, c2, dir, doubled)];
                if (0 == doubled) v += grid[r2][c2][inv[dir]];
                ans = min(ans, v);
            }
        }

        printf("Case %d: ", ++kase);
        if (ans == inf) printf("Impossible\n");
        else printf("%d\n", ans);
    }
    return 0;
}
posted @ 2018-02-20 12:06  不想吃WA的咸鱼  阅读(178)  评论(0编辑  收藏  举报