借鉴了陈峰在训练指南的思路, 表示感谢
提示: 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