反向边思想
例:http://poj.org/problem?id=3268
做法:跑两遍最短路,起点都是牛X(并没有骂人)
正向跑的时候相当于回家,反向跑的时候相当于去参加派对
分层图
我们可能遇到这样的图论模型:在一个正常的图上可以进行 k次决策,对于每次决策,不影响图的结构,只影响目前的状态或代价。同时这个图论模型和经典的最短路有关,这样我们可以考虑运用分层图最短路。
对于决策,将决策前的状态和决策后的状态间连接一条权值为决策代价的边,表示付出该代价转换了状态。
1 /* 2 分层图:(还有另一种方法:就是先这样一下,再那样...我不会 3 建第一层图时,随便多cope建 k 层相同的图,再将从上到下相邻的各图按照某一特殊权值连接(实际上是连点 4 遍历时基本没变化,只不过有可能需要注意把那个终点也连一下, 5 */ 6 #include<cstdio> 7 #include<queue> 8 using namespace std; 9 10 const int MAXN = 1200000; 11 const int MAXM = 6660000; 12 const int INF = 1000003647; 13 14 int n,m,k,cnt,s,t; 15 int vis[MAXN], dis[MAXN],head[MAXN]; 16 17 struct node{ 18 int id,dis; 19 node(int id = 0, int dis = 0) : id(id), dis(dis) {} 20 bool operator < (const node &xx) const { 21 return dis > xx.dis ; 22 } 23 }; 24 25 priority_queue <node> q; 26 27 struct edge{ 28 int y,val,next; 29 }e[MAXM]; 30 31 void add_edge(int x, int y, int val) { 32 e[++cnt].y = y; 33 e[cnt].next = head[x]; 34 e[cnt].val = val; 35 head[x] = cnt; 36 } 37 38 void dijkstra() { 39 int tmp2 = n*(k+1); 40 for(int i = 1; i <= tmp2; i++) dis[i] = INF; 41 q.push(node(s,0)); 42 dis[s] = 0; 43 while(!q.empty() ) { 44 node tmp = q.top() ; 45 q.pop() ; 46 int now = tmp.id ; 47 if(vis[now]) continue; 48 vis[now] = 1; 49 for(int i = head[now]; i; i = e[i].next ) { 50 int y = e[i].y; 51 if(dis[y] > tmp.dis + e[i].val ) { 52 dis[y] = tmp.dis + e[i].val ; 53 q.push(node(y,dis[y])); 54 } 55 } 56 } 57 } 58 59 60 int main() { 61 scanf("%d%d%d%d%d",&n,&m,&k,&s,&t); 62 s++,t++;//题目是从0开始的(这只是个人喜好... 63 int x,y,z; 64 for(int i = 1; i <= m; i++) { 65 scanf("%d%d%d",&x,&y,&z); 66 x++,y++; 67 add_edge(x, y, z); 68 add_edge(y, x, z);//在第1层加边(i)完成 69 //成败在此一举 70 for(int j = 1; j <= k; j++) {//一共有“k+1”层 71 72 add_edge(x+j*n, y+j*n, z);//cope第一层中的边i到各层(第j+1层) 73 add_edge(y+j*n, x+j*n, z); 74 add_edge(x+(j-1)*n, y+j*n, 0);//将第j层的x与第j+1层的y相连 75 add_edge(y+(j-1)*n, x+j*n, 0);//将第j层的y与第j+1层的x相连 76 //此题中分层图间的特殊权值是0 77 } 78 } 79 //处理终点,不然在这题中可能会出现免费次数没用完就已经到达了终点,这样就不能直接写dis[t+n*k]了 80 for(int i = 1; i <= k; i++) add_edge(t+(i-1)*n, t+i*n, 0); 81 dijkstra(); 82 printf("%d",dis[t+n*k]); 83 } 84 /*5 6 1 85 0 4 86 0 1 5 87 1 2 5 88 2 3 5 89 3 4 5 90 2 3 3 91 0 2 100*/
1 #include<cstdio> 2 #include<algorithm> 3 #include<queue> 4 //#include<cmath> 5 using namespace std; 6 const int MAXN = 300000; 7 const int MAXM = 600000; 8 const int INF = 2147000047; 9 10 int n,m; 11 int head[MAXM],dis[MAXM],vis[MAXM],cnt; 12 int S,T; 13 14 struct edge{ 15 int y,val,next; 16 }e[MAXM]; 17 18 struct idcard{ 19 int x,y,id1; 20 }e1[MAXN]; 21 22 bool cmp1(idcard x, idcard xx) { 23 return x.x < xx.x ||(x.x == xx.x && x.y < xx.y ); 24 } 25 26 bool cmp2(idcard x, idcard xx) { 27 return x.y < xx.y || (x.y == xx.y && x.x < xx.x ); 28 } 29 30 struct node{ 31 int id,dis; 32 node (int id = 0, int dis = 0) : id(id), dis(dis) {} 33 bool operator < (const node &xx) const { 34 return dis > xx.dis ; 35 } 36 }; 37 38 void add_edge(int x, int y, int val) { 39 e[++cnt].y = y; 40 e[cnt].next = head[x]; 41 e[cnt].val = val; 42 head[x] = cnt; 43 } 44 void ins(int x,int y,int val) 45 {add_edge(x,y,val); add_edge(y,x,val);} 46 //ps:本人一开始没写ins,好累.... 直到我看到了题解.... 47 void init_() { 48 sort(e1+1,e1+1+m+2,cmp1);//找 y纵向 相邻(找最近的,并不一定要都相连,等效就行)的点 ,并建图 49 for(int i = 1; i < m + 2; i++) 50 if(e1[i].x == e1[i+1].x ) 51 ins(e1[i].id1 ,e1[i+1].id1 , 2*(e1[i+1].y - e1[i].y ));//不用abs? 52 53 sort(e1+1,e1+1+m+2,cmp2);//找 x横向 相邻的,并建图 54 for(int i = 1; i < m + 2; i++) 55 if(e1[i].y == e1[i+1].y ) 56 ins(e1[i].id1 + m+2,e1[i+1].id1 + m+2,2*(e1[i+1].x - e1[i].x ) ); 57 //这是第二个图..(一次恍惚引起一桩两小时静态差错的惨案...)非常感谢樊同学用他以前犯相同错误的代码纠正我.. 58 59 } 60 priority_queue <node> q; 61 void dijkstra() { 62 for(int i = 1; i <= 2*(m+2); i++) dis[i] = INF; 63 dis[S] = 0; 64 q.push(node(S,0)); 65 while(!q.empty() ) { 66 node tmp = q.top() ; 67 q.pop() ; 68 int now = tmp.id ; 69 if(vis[now]) continue; 70 vis[now] = 1; 71 for(int i = head[now]; i; i = e[i].next ) { 72 int y = e[i].y ; 73 if(dis[y] > dis[now] + e[i].val ) { 74 dis[y] = dis[now] + e[i].val ; 75 q.push(node(y,dis[y])); 76 } 77 } 78 } 79 } 80 81 int main() { 82 scanf("%d%d",&n,&m); 83 for(int i = 1; i <= m + 2; i++) { 84 scanf("%d%d",&e1[i].x ,&e1[i].y ); 85 e1[i].id1 = i; 86 } 87 S = m+1, T = m+2;//按照存图顺序,加入起点和终点,起点为m+1,终点是m+2 88 /* 不用把每个网格里的点都存下来(不然开不下..... 89 考虑到当前点如果不是中转点,那他只能一直走到中转点或终点 90 所以可以存相邻的中转点(包括起点和终点:所以图中的点数要改变),以横向与纵向分层 91 */ 92 init_();//这是在图内连边 93 94 for(int i = 1; i <= m; i++) //这是把上下图相连 95 ins(i ,i + m+2, 1); 96 97 ins(m+1, 2*(m+1)+1, 0), ins(m+2, 2*(m+2), 0);//这是把两图的起点与起点,终点与终点连接 98 99 dijkstra();//最后的dijkstra 100 if(dis[T] == INF) { 101 printf("-1"); 102 return 0; 103 } 104 printf("%d" , dis[T]); 105 return 0; 106 }
其他最短路
1 #include<cstdio> 2 #include<algorithm> 3 #include<queue> 4 #include<vector> 5 using namespace std; 6 const int MAXN = 3000+99; 7 const int MAXM = 200000+99; 8 const int INF = 2147000047; 9 10 int n,m; 11 int dis[MAXN],real[MAXN],arrive[MAXN]; 12 /* arrrve[i] 表示走到i所需要的时间 13 real[i] 表示摧毁城市i的所有结界发生器所需要的时间 14 设j为所有i的前驱 , 则 real[i] = max(real[j]) 15 则i城市真正所需要的时间为 dis[i] = MAX(arrive[i], real[i]) 16 */ 17 18 int in[MAXN];//维护保护关系 : in[i] 表示城市i有多少个结界保护器 19 20 struct edge{ 21 int y,val,next; 22 }e[MAXM]; 23 int head[MAXN],vis[MAXN],cnt; 24 25 struct edge2{ 26 int next,y; 27 }g[MAXM]; 28 int headg[MAXN], cntg; 29 30 31 struct node{ 32 int id,dis; 33 node (int id = 0, int dis = 0) : id(id), dis(dis) {} 34 bool operator < (const node &xx) const { 35 return dis > xx.dis ; 36 } 37 }; 38 39 void add_edge(int x, int y, int val) { 40 e[++cnt].y = y; 41 e[cnt].next = head[x]; 42 e[cnt].val = val; 43 head[x] = cnt; 44 } 45 46 priority_queue <node> q; 47 48 void dijkstra() { 49 for(int i = 1; i <= n; i++) dis[i] = arrive[i] = INF; 50 dis[1] = arrive[1] = real[1] = 0; 51 in[1] = 0; 52 q.push(node(1,0)); 53 while(!q.empty() ) { 54 node tmp = q.top() ; 55 q.pop() ; 56 int now = tmp.id ; 57 if(vis[now]) continue; 58 vis[now] = 1;//注: 这里now的dis是已知的呢(因为我们求出来dis才入队 59 for(int i = head[now]; i; i = e[i].next ) { 60 int y = e[i].y ; 61 if(arrive[y] > dis[now] + e[i].val ) { 62 arrive[y] = dis[now] + e[i].val ; 63 if(!in[y]) {//只有入度为0才可以更新dis,然后入队呢 64 dis[y] = max(real[y], arrive[y]); 65 q.push(node(y,dis[y])); 66 } 67 } 68 } 69 70 for(int i = headg[now]; i; i = g[i].next) { 71 int y = g[i].y ; 72 real[y] = max(real[y], dis[now]); 73 in[y]--;//摧毁保护关系 74 if(!in[y]) { 75 dis[y] = max(real[y], arrive[y]); 76 q.push(node(y,dis[y])); 77 } 78 } 79 80 } 81 } 82 83 int main() { 84 scanf("%d%d",&n,&m); 85 int x,y,val; 86 for(int i = 1; i <= m; i++) { 87 scanf("%d%d%d",&x,&y,&val); 88 if(x == y) continue;//可能自己连自己 89 add_edge(x,y,val); 90 } 91 for(int i = 1; i <= n; i++) { 92 scanf("%d",&val); 93 while(val--) { 94 /*设i被y保护,我们从y建一条有向边到i,并记录i的入度in[i]。 95 广度遍历图,在摧毁y时,删去y到i的边,并更新入度。当in[i]=0时,方可进入城市i*/ 96 scanf("%d",&y); 97 in[i]++; 98 g[++cntg].next = headg[y]; 99 g[cntg].y = i;//(我实在不想再写add_edge_g了 100 headg[y] = cntg; 101 } 102 } 103 dijkstra(); 104 printf("%d",dis[n]); 105 return 0; 106 }
最短路计数
(发这个的目的就是怀恋下SPFA,以前写的,被翻了出来
• ans[i]表示从1到i的最短路径的条数,ans[]初值为0
• 在最短路过程中,从u到v,若dis[v] == dis[u] + val 则 ans[v]++
• 若dis[v] > dis[u] + val 则 dis[v] = dis[u] +val,ans[v] = 1
1 #include<cstdio> 2 #include<algorithm> 3 4 #define MAXN 1111111 5 #define MAXM 2222222 6 #define MOD 100003 7 int n,m,cnt; 8 int q[MAXN],vis[MAXN],dis[MAXN],head[MAXM]; 9 int ans[MAXN]; 10 11 struct edge{ 12 int y,next; 13 }e[MAXM]; 14 15 void add_edge(int x, int y) { 16 e[++cnt].y = y; 17 e[cnt].next = head[x]; 18 head[x] = cnt; 19 } 20 21 int main() { 22 scanf("%d%d",&n,&m); 23 int x,y; 24 for(int i = 1; i <= m; i++) { 25 scanf("%d%d",&x,&y); 26 add_edge(x,y); 27 add_edge(y,x); 28 } 29 for(int i = 1; i <= n; i++) dis[i] = 1; 30 int l = 1, r = 1; 31 q[1] = 1; 32 dis[1] = 0; 33 vis[1] = 1; 34 ans[1] = 1; 35 while(l <= r) { 36 int now = q[l++]; 37 vis[now] = 0; 38 for(int i = head[now]; i ; i = e[i].next ) { 39 int y = e[i].y; 40 if(dis[y] == dis[now] + 1) ans[y] += ans[now], ans[y] %= MOD; 41 if(!vis[y]) { 42 q[++r] = y; 43 vis[y] = 1; 44 dis[y] = dis[now] + 1; 45 ans[y] = ans[now]; 46 } 47 } 48 } 49 for(int i = 1; i <= n; i++) { 50 printf("%d ",ans[i]); 51 } 52 }
Other
根据中间点优化建边
一眼看上去像个靠谱的最短路,但数据范围有点上火…
---solor
怕上火,现在,喝加多宝,全国销量领先的红罐凉茶,改名加多宝,还是原来的配方,还是熟悉的味道。怕上火,喝加多宝!......
---virtualtan
需要优化建边(这题从xor入手
eg: 假设我们要从 001(二进制) 到 010 (二进制) ,我们要花费 2^0 + 2^1 的费用,我们可以先 001 -> 000, 再由 000 -> 010 ,算一下这样的费用是一样的
这里的“000” 就是中间点, 而再仔细看, 不能只找一个中间点 (如果是从 011 -> 001 ,我们如果还是上面的000做中间点, 费用就会变高
因此: 我们对于每个点 i,只需要建 i 到 i XOR 2^k 的边,之后跑最短路就可以了。
此外: 还要明确一点, 你按着上面的规则找好的中间点后,直接跑dij就行, 如011-> 100: 011 -> 001 -> 000 -> 100 (他们是等价的
注意: 要注意范围!中间点可能会大于 n
解决办法:1. 将 中间点判断一下, 调整到[1~n] ,再add_edge
2.图的范围调整为 [1, 2^k-1] k为满足2^k-1不小于n的情况下的最小值
ps: 本人在洛谷上样例没过, 希望有人看到后可以指出错误,而且开了O2的
1 #include<cstdio> 2 #include<algorithm> 3 #include<queue> 4 using namespace std; 5 #define MAXN 1000000+99 6 #define MAXM 5000000+99 7 const int INF = 2147000047; 8 9 int n,m,c; 10 int dis[MAXN],head[MAXN],vis[MAXN]; 11 int cnt; 12 13 struct node{ 14 int id, dis; 15 node (int id = 0, int dis = 0) : id(id), dis(dis) {} 16 bool operator < (const node &xx) const { 17 return dis > xx.dis ; 18 } 19 }; 20 priority_queue <node> q; 21 22 struct edge{ 23 int y,next,val; 24 }e[MAXM];//这是快捷通道 25 26 void add_edge(int x, int y, int val) { 27 e[++cnt].y = y; 28 e[cnt].val = val; 29 e[cnt].next = head[x]; 30 head[x] = cnt; 31 } 32 33 void dijkstra(int S) { 34 for(int i = 1; i <= n; i++) dis[i] = INF; 35 dis[S] = 0; 36 q.push(node(S, 0)); 37 while(!q.empty() ) { 38 node tmp = q.top() ; 39 q.pop() ; 40 int now = tmp.id ; 41 if(vis[now]) continue; 42 vis[now] = 1; 43 for(int i = head[now]; i; i = e[i].next ) { 44 int y = e[i].y ; 45 if(dis[y] > dis[now] + e[i].val ) { 46 dis[y] = dis[now] + e[i].val ; 47 q.push(node(y, dis[y])); 48 } 49 } 50 } 51 } 52 53 int main() { 54 scanf("%d%d%d",&n,&m,&c); 55 for(int i = 1, x, y, val; i <= m; i++) { 56 scanf("%d%d%d",&x,&y,&val); 57 add_edge(x,y,val); 58 } 59 60 for(int i = 1; i <= n; i++) 61 for(int k = 0; k <= 19; k++) {//calc: 100000 二进制有 17位 62 int to = i ^ (1 << k);//相差为 1 63 if(to <= n) add_edge(i, to, c * (1 << k)); 64 // 调整范围至 0 ~ n 65 } 66 67 int S,T; 68 scanf("%d%d",&S,&T); 69 dijkstra(S); 70 printf("%d",dis[T]); 71 return 0; 72 }