【NOIP2014模拟11.2A组】福慧双修
2018-01-26 16:08
411 查看
题意
给定一个图,要我们从1号点出发,经过一系列点后重新回到1点,求出最短距离是多少,限制是每条边只能通过一次。分析
对于这种求最短路径的题,我们一开始当然是想到SPFA,DIJ等一些列的求最短路算法了啦。但是怎么处理那些限制条件呢,能解决这个问题,就可以切了这题了。
首先,我们可以先把原图构建出来,跑一遍SPFA
求出每个点到1的最短距离dis[i],并设Pre[i]表示这条路径上与1相连的点的标号。
这样我们就可以通过这个Pre推测出一条边是否被使用过。
Pre表示,直接与1相连的点的值为其本身,其他的点的值等于其前驱结点的Pre值
得到这些后就可以构建新图了。
我们可以很清楚地知道,由于题目要求最后必须回到1号点,所以我们最好把1号点拆开成两个点,把新拆出的点记为n+1号点,,然后我们要把原图改一改(也就是建新图了).让我们枚举每条边,然后进行下列操作,
part 1:该边为x连向1,边权为value。
(1)当pre[x]!=x说明从1到x的最短路并没有经过这条边,所以这条边的可以被使用的,那么让我们在新图中建立一条从起点1到终点n+1的边,边权为value+dis[x]
(2)当pre[x]==x说明从1到x的最短路是使用了这条边的,所以在新图中建立该边的时候,边的权值不可以带上dis[x],那么让我们在新图中建立从x到终点n+1的边,边权为value.
Part2:该边为从1连向x,边权为value
(1)当pre[x]=x,说明从1到x的最短路使用了这条边,所以我们可以不建这条边.
(2)当pre[x]!=x,说明原点到达x的最短路径不是这条边,所以我们可以在新图中建立从1到x的边,边权为value.
Part3:该边的起点和终点均不为1.
(1)当pre[u]!=pre[v],说明原点到达两端点的最短路径是不同的,也就是说u到v的最短路径并不是通过这条边,这条可以填在新图中,所以我们可以在新图中建立一条从1到v,边权为dis[u]+value.
(2)当pre[u]==pre[v],在新图中保留原边。
最后的输出dis[1 + n]即可
这里连边比较复杂,我用了数据结构去连边。参考代码如下
#include <cstdio> #include <iostream> #include <cmath> #include <cstring> #include <algorithm> #define N 200005 #define fo(i,a,b) for (int i=a;i<=b;i++) #define fd(i,a,b) for (int i=a;i>=b;i--) #define INF 214748364 using namespace std; int Final ,n,m; int dis ,d ,Pre ; bool Flag ; struct node { int to,next,time; node(void){} node(int a,int b,int c) : to(a),next(b),time(c){} }; struct Q { node Line ; int Final ,tot; }e,e2; int read(int &n) { char ch = ' '; int q = 0, w = 1; for (;(ch != '-') && ((ch < '0') || (ch> '9'));ch = getchar()); if (ch == '-') w = -1,ch = getchar(); for (; ch >= '0' && ch <= '9';ch = getchar()) q = q * 10 + ch - 48; n = q * w; return n; } void Link(Q &e,int x,int y,int z) { e.Line[++ e.tot] = node(y,e.Final[x],z),e.Final[x] = e.tot; } void Spfa(Q &e) { int l = 0 ,r = 1; d[1] = 1; Flag[1] = true; while (l < r) { int x = d[++ l]; for (int i = e.Final[x];i;i = e.Line[i].next) { int k = e.Line[i].to; if (dis[x] + e.Line[i].time < dis[k]) { dis[k] = dis[x] + e.Line[i].time; if (x == 1) Pre[k] = k; else Pre[k] = Pre[x]; if (!Flag[k]) { Flag[k] = true; d[++ r] = k; } } } Flag[x] = false; } } int main() { read(n);read(m); fo(i,1,m) { int s,t,v,w; read(s),read(t),read(v),read(w); Link(e,s,t,v); Link(e,t,s,w); } fo(i,1,n) dis[i] = INF; dis[1] = 0; Spfa(e); fo(i,1,n) { int x = i; for (int k = e.Final[x];k;k = e.Line[k].next) { int y = e.Line[k].to; if (y == 1) { if (Pre[x] == x) Link(e2,x,n + 1,e.Line[k].time); else Link(e2,1,n+1,dis[x] + e.Line[k].time); } else if (x == 1) { if (Pre[y] != y) Link(e2,1,y,e.Line[k].time); } else if (Pre[y] == Pre[x]) Link(e2,x,y,e.Line[k].time); else Link(e2,1,y,dis[x] + e.Line[k].time); } } fo(i,1,n + 1) dis[i] = INF; dis[1] = 0; Spfa(e2); if (dis[n + 1] == INF) printf("-1\n"); else printf("%d\n",dis[n + 1]); return 0; }
相关文章推荐
- 3917 【NOIP2014模拟11.2A组】福慧双修 (Standard IO)题解
- 【NOIP2014模拟11.2A组】国色天香 (Standard IO)
- 【NOIP2014模拟11.6】射击
- jzoj3928【NOIP2014模拟11.6】射击(贪心)
- 3927. 【NOIP2014模拟11.6】可见点数 (Standard IO)
- JZOJ 3824【NOIP2014模拟9.9】渴
- 【NOIP2014模拟9.9】遇见
- NOIP2014模拟 8.13
- 3785. 【NOIP2014模拟8.19】分数
- jzoj3929【NOIP2014模拟11.6】创世纪(图论,bfs,贪心)
- 3928. 【NOIP2014模拟11.6】射击 (Standard IO)
- JZOJ 3809 【NOIP2014模拟8.25】设备塔
- JZOJ 3775. 【NOIP2014模拟8.15】因子的排列
- [JZOJ 3794]. 【NOIP2014模拟8.20】高级打字机
- 高中OJ 3793. 【NOIP2014模拟8.20】数字对
- NOIP2014模拟 8.12
- NOIP2014模拟8.21
- 【NOIP2014模拟8.15】城市街区
- JZOJ 3899. 【NOIP2014模拟】逻辑的连通性
- 3929. 【NOIP2014模拟11.6】创世纪 (Standard IO)