JZOJ 3086. 回家
题目
分析
-
此题的决策为转向,由于只存在横向和纵向两个放学,我们对这两个方向分别建立一层。即一层只连原图横向边,一层只连纵向边。
对于转向这个决策,将决策前的状态和决策后的状态间连接一条权值为决策代价的边,表示付出该代价转换了状态。
在本题中,即上下两层对应点连接一条权值为1的边,层内边权均为2.
然后跑最短路即可。
- 简单点讲,就是首先将同一横排与竖排上下所有点连边
- 然后交汇点其实就是一个转向的点
- 将交汇的点x与y连接跑最短路即可
代码
1 #include<iostream> 2 #include<queue> 3 #include<cstring> 4 #include<algorithm> 5 #define M 800001 6 #define N 200001 7 #define inf 0x3f 8 using namespace std; 9 struct sb 10 { 11 int x,y,bh; 12 }a[N]; 13 struct edge 14 { 15 int next,to,val; 16 }e[M]; 17 int n,m; 18 bool cmp(sb a,sb b) 19 { 20 if (a.x==b.x) return a.y<b.y; 21 return a.x<b.x; 22 } 23 bool CMP(sb a,sb b) 24 { 25 if (a.y==b.y) return a.x<b.x; 26 return a.y<b.y; 27 } 28 int h[M],cs=1; 29 void add(int from,int to,int val) 30 { 31 e[cs].to=to; 32 e[cs].next=h[from]; //将它指向对应的下一层 33 e[cs].val=val; 34 h[from]=cs++; 35 } 36 void ins(int from,int to,int val) 37 { 38 add(from,to,val); 39 add(to,from,val); 40 } 41 queue <int> q; 42 int dis[M]; 43 bool v[M]; 44 void spfa() 45 { 46 memset(dis,0x3f,sizeof(dis)); 47 dis[m+1]=0; 48 v[m+1]=1; 49 q.push(m+1); 50 while (!q.empty()) 51 { 52 int x=q.front(); q.pop(); v[x]=0; 53 for (int i=h[x];i;i=e[i].next) 54 { 55 int to=e[i].to,val=e[i].val; 56 if (dis[to]>dis[x]+val) 57 { 58 dis[to]=dis[x]+val; 59 if (!v[to]) 60 { 61 v[to]=1; 62 q.push(to); 63 } 64 } 65 } 66 } 67 } 68 int main () 69 { 70 cin>>n>>m; 71 for (int i=1;i<=m+2;i++) 72 { 73 cin>>a[i].x>>a[i].y; 74 a[i].bh=i; 75 } 76 sort(a+1,a+m+3,cmp); 77 for (int i=1;i<=m+2;i++) 78 if (a[i].x==a[i+1].x) ins(a[i].bh,a[i+1].bh,2*(a[i+1].y-a[i].y)); //将同一行的的x连边,权值为2*(y1-y2) 79 sort(a+1,a+m+3,CMP); 80 for (int i=1;i<=m+2;i++) 81 if (a[i].y==a[i+1].y) ins(a[i].bh+m+2,a[i+1].bh+m+2,2*(a[i+1].x-a[i].x)); //同一列同上 82 for (int i=1;i<=m;i++) 83 ins(i,i+m+2,1); //将转车点x与y连边,权值为1 84 ins(m+1,2*m+3,0); ins(m+2,2*m+4,0); //终点,起点为0 85 spfa(); 86 if (1061109567==dis[m+2]) 87 cout<<-1; 88 else 89 cout<<dis[m+2]; 90 }
为何要逼自己长大,去闯不该闯的荒唐