UVALive 4128 Steam Roller(最短路(拆点,多状态))

题意:模拟了汽车的行驶过程,边上的权值为全速通过所消耗的时间,而起步(从起点出发的边)、刹车(到终点结束的边)、减速(即将拐弯的边)、加速(刚完成拐弯的边)这四种不能达到全速的情况,消耗的时间为权值*2。问从起点到终点所消耗的最少时间。

这道题主要是建图,很复杂,无耻地照着书上的代码码了一遍。让状态搞糊涂了= =

注意:

1、grid[][][4]记录了点的上下左右四条边的权值,id[][][4][2]记录各个点。

2、到一个点的最短路可以是路过这个点再折返回来,e.g:1->2 c=17,2->3 c=4,从1->2的最短路为17+8+8=33<34

3、原图总点数上限100*100,而拆点后共有8*100*100个点,狠狠地RE了一发。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<vector>
  4 #include<queue>
  5 #include<algorithm>
  6 using namespace std;
  7 
  8 const int MAXN=111111;
  9 const int maxr=111,maxc=111;
 10 const int INF =1e9;
 11 
 12 const int UP=0,LEFT=1,DOWN=2,RIGHT=3;
 13 const int inv[]={2,3,0,1};
 14 const int dr[]={-1,0,1,0};
 15 const int dc[]={0,-1,0,1};
 16 
 17 struct Edge{
 18     int u,v,c;
 19 };
 20 
 21 struct HeapNode{
 22     int c,u;
 23     bool operator < (const HeapNode rhs)const {
 24         return c>rhs.c;
 25     }
 26 };
 27 
 28 struct Dijkstra{
 29     int n,m;
 30     vector<Edge>edges;
 31     vector<int>G[MAXN];
 32     bool vis[MAXN];
 33     int d[MAXN],p[MAXN];
 34 
 35     void init(int n)
 36     {
 37         this->n=n;
 38         for(int i=0;i<n;i++)
 39             G[i].clear();
 40         edges.clear();
 41     }
 42 
 43     void add(int u,int v,int c)
 44     {
 45         edges.push_back((Edge){u,v,c});
 46         m=edges.size();
 47         G[u].push_back(m-1);
 48     }
 49 
 50     void dijkstra(int st)
 51     {
 52         priority_queue<HeapNode>q;
 53         for(int i=0;i<n;i++)
 54             d[i]=INF;
 55         d[st]=0;
 56         memset(vis,0,sizeof(vis));
 57         q.push((HeapNode){0,st});
 58         while(!q.empty())
 59         {
 60             HeapNode x=q.top();q.pop();
 61             int u=x.u;
 62             if(vis[u])
 63                 continue;
 64             vis[u]=true;
 65             int sz=G[u].size();
 66             for(int i=0;i<sz;i++)
 67             {
 68                 Edge e=edges[G[u][i]];
 69                 if(d[e.v]>d[u]+e.c){
 70                     d[e.v]=d[u]+e.c;
 71                     q.push((HeapNode){d[e.v],e.v});
 72                 }
 73             }
 74         }
 75     }
 76 };
 77 
 78 int grid[maxr][maxc][4];
 79 
 80 int n,id[maxr][maxc][4][2];
 81 
 82 int ID(int r,int c,int dir,int doubled)
 83 {
 84     int& x=id[r][c][dir][doubled];
 85     if(x==0)x=++n;
 86     return x;
 87 }
 88 
 89 int R,C;
 90 
 91 bool cango(int r,int c,int dir)
 92 {
 93     if(r<0||r>=R||c<0||c>=C)
 94         return false;
 95     return grid[r][c][dir]>0;
 96 }
 97 
 98 Dijkstra solver;
 99 
100 int readint()
101 {
102     int x;
103     scanf("%d",&x);
104     return x;
105 }
106 
107 int main()
108 {
109     int r1,c1,r2,c2,kase=0;
110     while(scanf("%d%d%d%d%d%d",&R,&C,&r1,&c1,&r2,&c2)==6&&R)
111     {
112         r1--;c1--;r2--;c2--;
113         memset(grid,0,sizeof(grid));
114         for(int r=0;r<R;r++)//每次更新点(r,c)的下方和右方的边
115         {
116             for(int c=0;c<C-1;c++)
117                 grid[r][c][RIGHT]=grid[r][c+1][LEFT]=readint();
118             if(r!=R-1)
119                 for(int c=0;c<C;c++)
120                     grid[r][c][DOWN]=grid[r+1][c][UP]=readint();
121         }
122 
123         solver.init(R*C*8+1);
124 
125         n=0;
126         memset(id,0,sizeof(id));
127 
128         for(int dir=0;dir<4;dir++)//对起点做double
129             if(cango(r1,c1,dir))
130                 solver.add(0,ID(r1+dr[dir],c1+dc[dir],dir,1),grid[r1][c1][dir]*2);
131 
132         for(int r=0;r<R;r++)//要把所有点都处理完整,不能到r2,c2就结束
133             for(int c=0;c<C;c++)
134                 for(int dir=0;dir<4;dir++)
135                     if(cango(r,c,inv[dir]))
136                         //为什么是inv?grid[r][c][inv[dir]]是点(r,c)沿inv[dir]方向到点x的边权,同样也是点x->点(r,c)的边权:老边
137                         //而区别在于判断进入(r,c)的边,与出(r,c)进(new,r,newc)的边方向是否一致
138                         for(int newdir=0;newdir<4;newdir++)
139                             if(cango(r,c,newdir))
140                                 for(int doubled=0;doubled<2;doubled++){
141                                     int newr=r+dr[newdir];
142                                     int newc=c+dc[newdir];
143                                     int v=grid[r][c][newdir],newdoubled=0;
144                                     if(dir!=newdir){//若方向不一致,两条边都要加倍
145                                         if(!doubled)//对于前一条边分两种情况讨论:已经double(之前已经加速或起步),未double
146                                             v+=grid[r][c][inv[dir]];//
147                                         newdoubled=1;
148                                         v+=grid[r][c][newdir];
149                                     }
150                                     solver.add(ID(r,c,dir,doubled),ID(newr,newc,newdir,newdoubled),v);
151                                     //若方向一致,连到newdouble==0的两条边权值相同
152                                     //否则,连到newdouble==1的两条边相差grid[r][c][inv[dir]],即分开讨论老边是否已加倍,由于最后求最短路,不影响
153                                 }
154         solver.dijkstra(0);
155 
156         int ans=INF;
157         for(int dir=0;dir<4;dir++)//从与终点(r2,c2)相连的八个状态中取最小值
158             if(cango(r2,c2,inv[dir]))
159                 for(int doubled=0;doubled<2;doubled++)
160                 {
161                     int v=solver.d[ID(r2,c2,dir,doubled)];
162                     if(!doubled)
163                         v+=grid[r2][c2][inv[dir]];
164                     ans=min(ans,v);
165                 }
166 
167         printf("Case %d: ",++kase);
168         if(ans==INF)
169             printf("Impossible\n");
170         else
171             printf("%d\n",ans);
172     }
173     return 0;
174 }
View Code

 后记:

  这里的数组id[][][][],以及函数 ID()的处理很值得学习一下。在处理多状态等复杂情况是很实用。

  用这个方法自己写了一道题,一遍就通过了样例,好有成就感,可惜uva在这道题上又挂了= =

posted @ 2013-08-23 21:02  Thousand Sunny  阅读(436)  评论(0编辑  收藏  举报