UVa 1078 Steam Roller

借鉴了陈峰在训练指南的思路, 表示感谢

提示: 1. 直接求最短路是有困难的, 因为状态不满足独立性 , 所以拆点

    2. 将(x , y)细分成 , (x , y , dir , doubled)  , 后两个分别为到这个点时的方向、这条边是否两次计算了

    3. 将每一个状态用一个id 函数投射到一个具体的数值 , 然后预处理所有状态之间的边(处理好那些double) , 这样求最短路就是一个独立的过程

    4. 开头的源点不好把握 , 所以特殊处理 , 将第一次转移拿出来 ; 但结尾的统计是简单的


注意: 为了方便各种mod , 和计算id , 使用从0开始的坐标会更简单 , 如果有人看过训练指南的代码仓库这个题的代码 , 就会发现写的很麻烦 , 这种题目一定要注意 , 充分利用状态 , 不要给自己添麻烦 , 详情见代码


//
//  main.cpp
//  UVa 1078
//
//  Created by Fuxey on 15/10/7.
//  Copyright © 2015年 corn.crimsonresearch. All rights reserved.
//

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <deque>
#include <set>
#include <map>
#include <string>
#include <algorithm>
#include <list>
#include <queue>

using namespace std;
struct edge
{
    int t , v;
    edge(int t = 0, int v = 0):t(t),v(v){}
};
struct state
{
    int p , d;
    state(int p = 0, int d = 0):p(p),d(d){}
    bool operator <(const state& b)const { return d>b.d; }
};
const int maxnode=  8e4+1e3;
const int INF = 1<<29;
int e[110][110][2];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
int rev[]={1,0,3,2};
int n , m , sx , sy , ex , ey;

vector<edge> g[maxnode];
int d[maxnode] , book[maxnode];

int id(int x , int y , int i, int j){ if(!(x>=0 && x<n && y>=0 && y<m)) return -1;  return ((x*m+y)*4+i)*2+j;}  // i ->dir , j-?doubled
void reid(int p)
{
    int d = p%2; p/=2; int dir = p%4; p/=4; int y = p%m; int x = p/m;
    cout<<x<<" "<<y<<" "<<dir<<" "<<d<<endl;
}
int main(int argc, const char * argv[]) {

    int Case=0;
    while(cin>>n>>m>>sx>>sy>>ex>>ey && n+m)
    {
        sx--; sy--; ex--; ey--;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m-1;j++) scanf("%d",&e[i][j][0]);
            if(i!=n-1)
                for(int j=0;j<m;j++) scanf("%d",&e[i][j][1]);
        }
        
        for(int i=0;i<8*n*m;i++) g[i].clear();
        for(int i=0;i<n;i++) for(int j=0;j<m;j++) for(int dir = 0;dir<4;dir++) for(int d=0;d<2;d++)
            for(int dir2 = 0;dir2<4;dir2++) for(int d2=0;d2<2;d2++)
            {
                int p1 = id(i, j, dir, d);
                int p2 = id(i+dx[dir2], j+dy[dir2], dir2, d2);
                if(p2==-1) continue;
                if((d==0 || d2==0) && dir!=dir2) continue;
                
                int ndir = dir2 , X = i , Y = j , V;
                if(ndir!=0 && ndir!=2){ ndir = rev[ndir]; X = i+dx[dir2]; Y = j+dy[dir2]; }
                ndir = (ndir==0?0:1);
                V = e[X][Y][ndir];
                if(V==0) continue;
                if(d2) V*=2;
                g[p1].push_back(edge(p2 , V));
            }
        
        fill(d, d+8*n*m, INF);
        fill(book, book+8*n*m, 0);
        priority_queue<state> q;
        int ss = id(sx, sy, 0, 1);
        for(int i=0;i<g[ss].size();i++) if(g[ss][i].t%2)
        {
            int t = g[ss][i].t;
            d[t] = g[ss][i].v;
            q.push(state(t , d[t]));
        }
        
        while(!q.empty())
        {
            state u = q.top();
            q.pop();
            
            if(book[u.p]) continue;
            book[u.p] = 1;
            for(int i=0;i<g[u.p].size();i++)
            {
                int t = g[u.p][i].t;
                int v = g[u.p][i].v;
                if(d[t] > d[u.p]+v)
                {
                    d[t] = d[u.p]+v;
                    q.push(state(t , d[t]));
                }
            }
        }
        int res = INF;
        for(int dir = 0;dir<4;dir++)
        {
            int p = id(ex, ey, dir, 1);
            res = min(res, d[p]);
        }
        cout<<"Case "<<++Case<<": ";
        if(res==INF) cout<<"Impossible\n";
        else cout<<res<<endl;
    }
    
    
    return 0;
}

Ps: 这是最裸的 , 不使用邻接表 , 不手写队列的版本 , 估计都实现可以速度快上两倍 , 有兴趣的可以自己实现

文章有任何纰漏 , 或者有任何问题 , 请告知博主: QQ:812483101 

你可能感兴趣的:(最短路,uva)