ccf 2017 12 - 4 行车路线
附上代码:
#include<stdio.h> #include<string.h> #define inf 0x7fffffff #include<iostream> #include<algorithm> #include<queue> using namespace std; typedef long long ll; const int MAXM = 1e6 + 5; int n, m; typedef struct NODE { int to; ll temp; ll cost; int ff; friend bool operator < (const NODE &a, const NODE &b) { return a.cost > b.cost; } }node; typedef struct Edge { int from;///起点 ll temp;///记录权值 int f;///标记小路 int next; int to; ll w; } edge; edge maps[MAXM]; int head[505]; ll dist[505]; int vids[505]; int cnt; void creat () { for(int i=0; i<505; i++) head[i]=-1; cnt=0; } void add(int t,int u,int v,ll w) { maps[cnt].f = t; maps[cnt].from = u; maps[cnt].to=v; maps[cnt].w=w; maps[cnt].next=head[u];///一开始放置为-1,-1的条件就可以跳出 ///下一步接着储存,head[u]的值,就是前面的位置 head[u]=cnt++;///head[u]会得到这条线的值 } ll dijkstra(int s,int t) { for(int i=0; i<505; i++) { vids[i]=0; dist[i]=inf; } priority_queue<node>que; node begins; begins.to = s; begins.temp = 0; begins.cost = 0; begins.ff = 0; que.push(begins); while(!que.empty()) { node ends=que.top(); que.pop(); if(!vids[ends.to]) { vids[ends.to]=1; dist[ends.to]=ends.cost; for(int i = head[ends.to]; ~i; i = maps[i].next) { int to = maps[i].to; ll w = maps[i].w; int flag = maps[i].f; if(!vids[to]) { node ans; ans.to=to; if(ends.ff == 0 && flag == 0) { ans.cost = ends.cost + w; ans.ff = flag; ans.temp = 0; } else if(ends.ff == 1 && flag == 0) { ans.cost = ends.cost + w; ans.ff = flag; ans.temp = 0; } else if(ends.ff == 1 && flag == 1) { ans.cost = ends.cost - (ends.temp * ends.temp) + (ends.temp + w) * (ends.temp + w); ans.ff = flag; ans.temp = ends.temp + w; } else if(ends.ff == 0 && flag == 1) { ans.cost = ends.cost + (w * w); ans.ff = flag; ans.temp = w; } que.push(ans); } } } } if(dist[t] == inf) { return -1; } else { return dist[t]; } } int main() { while(~scanf("%d%d",&n, &m)) { creat(); int t, a, b; long long c; for (int i = 0; i < m; i ++) { scanf("%d%d%d%lld",&t, &a, &b, &c); add(t, a, b, c); add(t, b, a, c); } ll re = dijkstra(1, n); printf("%lld\n",re); } return 0; }
#include <iostream> #include <queue> #include <cstring> #include <algorithm> #include <cstdio> using namespace std; typedef long long LL; class Edge{ public: Edge(){}/*无参构造函数 */ public: int u, v, w, next, flag; /* u - 起点 v - 终点 w - 边权 next - 同个起点的前一条边 flag - 标记是否是小路 */ }; class Malash_map{ public: Malash_map(){ } void Init(){ for(int i = 0; i < 505; i ++){ head[i] = -1; } tot = 0; }//初始化链式前向星 void add(int u, int v, int w, int flag){//添加一条单向边 arr[tot].u = u, arr[tot].v = v, arr[tot].w = w, arr[tot].flag = flag; arr[tot].next = head[u], head[u] = tot ++; } public: int head[505], tot; Edge arr[int(2e5 + 500)]; };//链式前向星 int vids[505];//标记这个点是否已经被拓展过 LL dist[505];//从1到n这个点的最短路径 Malash_map M; class Node{ public: Node(int _v, int _f, LL _min_d, LL _cost):v(_v), f(_f), min_dist(_min_d), cost(_cost){} friend bool operator< (const Node &a, const Node &b){ return a.cost > b.cost; }//将小于号重载于大于号,那么就能保证优先队列维护的是较小的路径先出队列 public: int v, f; LL min_dist, cost; }; LL dijkstra(int s, int t){ for(int i = 0; i < 505; i ++){ vids[i] = 0, dist[i] = __LONG_LONG_MAX__; }//初始化,肯定所有的点没有被访问过,所有的距离都是无穷远 priority_queue<Node>que;//优先队列,维护较短的路径先出队列 while(!que.empty()) que.pop();//清空优先队列 Node begins(s, 0, 0, 0);//构造起点 que.push(begins);//将起点放入优先队列 while(!que.empty()){ Node temp = que.top(); que.pop();//取出对应的优先队列中的第一个点,即当前到某个点的最短路径 if(vids[temp.v] == 1){//如果这个点被拓展过,那么证明这个点的最短路径 //以及以这个点为起点的边肯定都被拓展过,那么就没有必要再拓展一次 continue; } vids[temp.v] = 1;//标记这个点已经被拓展 dist[temp.v] = min(dist[temp.v], temp.cost);//保留路径最小值 if(temp.v == t){//如果已经找到了终点,那么没有必要再去拓展了 //拓展其他都没有必要,所以可以剪枝做一个小优化,直接返回结果 return dist[t]; } for(int i = M.head[temp.v]; i != -1; i = M.arr[i].next){ //开始拓展以temp.v为起点的所有相邻边,然后构造出对应的Node新点 Node en(M.arr[i].v, M.arr[i].flag, temp.min_dist, temp.cost); if(!vids[en.v]){//如果这个新点没有被走过,即没有拓展出最短路径 //那么这个点就应该被拓展 if((temp.f == 0 || temp.f == 1) && en.f == 0){ //考虑从大路->大路,小路->大路,处理的结果都是一样的如下 en.min_dist = 0, en.cost += M.arr[i].w; //将现在连续走的小路的总数变为0,然后更新对应的cost花费 } else{ en.cost = en.cost - (en.min_dist * en.min_dist) + (en.min_dist + M.arr[i].w) * (en.min_dist + M.arr[i].w); //假设连续走了的小路和为(a + b + c) //那么又走了段小路d //那么将前面得到的cost - (a + b + c)的平方 + (a + b + c + d)的平方,得到的是到当前的点的最小值 //然后更新练习走了的小路的和为(a + b + c + d) en.min_dist += M.arr[i].w; } que.push(en);//将得到的点放入优先队列,优先队列会保证每次取出优先队列中最小的点,进行拓展。 } } } return dist[t];//返回答案 } int main(){ int n, m; ios::sync_with_stdio(false); cin.tie(nullptr); while(cin >> n >> m){ M.Init(); for(int i = 0; i < m; i ++){ int f, u, v, w; cin >> f >> u >> v >> w; M.add(u, v, w, f);//添加双向边 M.add(v, u, w, f); } LL ans = dijkstra(1, n); cout << ans << endl; } return 0; }