题意:有一个n条竖线m条横线组成的网络,你的任务是开着一辆蒸汽压路机,用最短的时间从一个点到另一个点,其中一些线段上的权值表示压路机全速前进所用的时间,0表示不能通过,由于蒸汽机的惯性较大所以在转弯前和转弯后那条边所走的实际速度都是全速前进速度的两倍。开始后和结束前的边也是两倍。时间加倍的规则是不会叠加的。最后给你两个顶点求最短路。
思路:这道题难点就是建图了,显示自己思考想了好多种方法但是最后在实现的时候都发现思路比较乱,写到最后还是放弃了。最后选用了liurujia书上的第二种方法,主要是比较简单又好理解。其中的一个4号定点表示静止点,0-3分别表示四个方向。四个方向的顶点只有和自己同一方向的节点才会有边联系,若是想要转向则先改变成静止状态,然后再变成相应的方向。这样的话从启动到静止,再到启动正好前后两条边都增加了两遍。另外书上说的一个点走一步后就静止了会出现和题意不符合的情况,这种情况需要单独建边。这样为了方便最短路的算法执行,我们再加一个0号定点指向出发点。这样只要执行一次Dijkstra算法就行了。
另外,从liurujia的树上有学到了一种很好的借点标号的方式,他是开了一个数组,标记每个点的映射值,若是没有标记为0。(所以是从1开始的)原来我都是根据一个顶点拆分成多少个点来标记,这样往往还要通过公式来换算所以不太方便。这个方法就显得非常方便而且不容易出错又灵活,在建完图之后定点的个数就自动确定了,也不用自己计算。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <algorithm> 6 #include <stack> 7 #include <queue> 8 #include <vector> 9 #include <cmath> 10 #define MP(a, b) make_pair(a, b) 11 #define PB(a) push_back(a) 12 13 using namespace std; 14 15 typedef pair<int, int> pii; 16 typedef long long ll; 17 18 const int INF = 0x3f3f3f3f; 19 const double eps = 1E-6; 20 const int LEN = 100010; 21 const int MLEN = 110; 22 const int UP = 0, LEFT = 1, DOWN = 2, RIGHT = 3; 23 const int xx[] = {-1, 0, 1, 0}; 24 const int yy[] = { 0,-1, 0, 1}; 25 int dis[LEN], n, m, sx, sy, ex, ey, N; 26 int edg[MLEN][MLEN][4], id[MLEN][MLEN][5]; 27 vector<pii> Map[LEN]; 28 29 //change the id 这个函数第一次见感觉自动编号还是很好,以前都是通过一套公式换算比较烦。 30 inline int ch(int rp, int cp, int dip){ 31 int &x = id[rp][cp][dip]; 32 if(x==0)return x = ++N; 33 else return x; 34 } 35 inline bool je(int x, int y, int dip) 36 { 37 if(x<1 || x>n || y<1 || y>m) return false; 38 else return edg[x][y][dip]>0; 39 } 40 inline int readi(){int x; scanf("%d", &x);return x;} 41 42 void Dijkstra(int s) 43 { 44 priority_queue<pii, vector<pii>, greater<pii> > q; 45 int vis[LEN] = {0}; 46 memset(dis , 0x3f, sizeof dis); 47 dis[s] = 0; 48 q.push(MP(dis[s], s)); 49 while(!q.empty()) 50 { 51 pii nvex = q.top(); q.pop(); 52 int nv = nvex.second; 53 if(vis[nv])continue; 54 vis[nv] = 1; 55 for(int i=0; i<Map[nv].size(); i++){ 56 int x = Map[nv][i].first, y = Map[nv][i].second; 57 if(dis[x]>dis[nv]+y){ 58 dis[x] = dis[nv]+y; 59 q.push(MP(dis[x], x)); 60 } 61 } 62 } 63 64 } 65 66 void readmap() 67 { 68 for(int i=0; i<LEN; i++)Map[i].clear(); 69 memset(edg, 0, sizeof edg); 70 for(int i=1; i<n; i++){ 71 for(int j=1; j<m; j++) edg[i][j+1][LEFT] = edg[i][j][RIGHT] = readi(); 72 for(int j=1; j<=m; j++) edg[i][j][DOWN] = edg[i+1][j][UP] = readi(); 73 } 74 for(int i=1; i<m ;i++)edg[n][i+1][LEFT] = edg[n][i][RIGHT] = readi(); 75 } 76 77 void buildmap() 78 { 79 memset(id, 0 ,sizeof id); 80 N = 0; 81 //建立起点标号为0 82 for(int i=0; i<4; i++)if(je(sx,sy,i)){ 83 Map[0].PB(MP(ch(sx+xx[i],sy+yy[i], i), edg[sx][sy][i]*2)); 84 Map[0].PB(MP(ch(sx+xx[i],sy+yy[i], 4), edg[sx][sy][i]*2));//书上说的走一步静止的情况 85 } 86 for(int i=1; i<=n; i++){ 87 for(int j=1; j<=m; j++){ 88 for(int k=0; k<4; k++){ 89 if(je(i,j,(k+2)%4)){ 90 Map[ch(i,j,k)].PB(MP(ch(i,j,4),edg[i][j][(k+2)%4])); //从走动到静止 91 if(je(i,j,k))Map[ch(i,j,k)].PB(MP(ch(i+xx[k],j+yy[k],k), edg[i][j][k]));//沿着一个方向比直走 92 } 93 if(je(i,j,k)){ 94 Map[ch(i,j,4)].PB(MP(ch(i+xx[k],j+yy[k],k),edg[i][j][k]*2));//静止到转向 95 Map[ch(i,j,4)].PB(MP(ch(i+xx[k],j+yy[k],4),edg[i][j][k]*2));//静止到静止 96 } 97 } 98 } 99 } 100 } 101 102 int main() 103 { 104 // freopen("in.txt", "r", stdin); 105 106 int kase = 1; 107 while(scanf("%d%d%d%d%d%d", &n, &m, &sx, &sy, &ex, &ey)==6 && n){ 108 readmap(); 109 buildmap(); 110 Dijkstra(0); 111 int ans = dis[ch(ex,ey,4)]; 112 printf("Case %d: ", kase++); 113 if(ans!=INF) printf("%d ", ans); 114 else printf("Impossible "); 115 } 116 return 0; 117 }