1. 程式人生 > >CCF 201712-04 行車路線_Dijkstra變形

CCF 201712-04 行車路線_Dijkstra變形

201712-04行車路線 傳送門

這看似是一個很複雜的問題, 實際上, 額…

首先它有基礎的最短路問題的影子,題目有20%的資料是沒有小道的,可用Dijkstra演算法直接求那麼本著那更多的部分分的戰略, 我們先寫基礎程式(先假設全部都是大道)

OK, 初稿裸的dijkstra是20分, 那麼考試的時候這20分也比較穩了, 距離滿分也只有80分罷了… 罷了… 罷了…

檢視資料描述:

對於30%的評測用例,1 ≤ n ≤ 8,1 ≤ m ≤ 10 這句話告訴我們即使想不出正解, 正確的暴力也是可以得到30分的.

對於另外20%的評測用例,所有的小道不相交 小道不相交意味著什麼?意味著我們不用考慮連續的小道,那麼不用正解也是可以的到這40分的, 因為這樣的話, 小道的權值就直接變成它長度的平方了.那麼開始水第二版

OK, 第二版提交上去,得了60分了,並沒有花多少功夫,也不難. 所以按照這種難度上300分還是挺有希望的

那麼剩下的資料已經不算小了1 ≤ n ≤ 500,1 ≤ m ≤ 105,1 ≤ a, b ≤ n,t是0或1,c ≤ 105。,要拿滿分必定是要正解的, 雖然這個資料來看可能並不需要優化.

剩下的問題就是,如何在最短路問題中,解決小道可能連起來的問題.乍一看很玄,沒什麼思路.

我想, 獲取可以複雜化這個dis陣列,改成結構體,儲存距離的同時儲存它的大小道性質以及小道長度. 這樣的話,那麼從一個結點到另一個結點,這個結點是小道還是大道會影響後面最短路的選擇嗎?

似乎是會的: 思考這樣一種情況: 現在從A節點更新到B節點,A結點選的是小道,這種情況下A-B選大道更優,可是實際情況是,如果A結點選大道,A-B選小道可以獲得更優的結果.那麼得不出正確的解.

後面的最優解可能是從前面的次優解繼承過來的. 但我們可以發現,能夠利用的A結點資訊最多隻有兩種,分別是大道和小道,那麼我們是否可以同時對一個儲存這兩種路徑的解呢? 應該是可行的.直覺告訴我,這就是正解了.

結果得了70, 這麼複雜的程式, 到頭來也不知道是我思路錯了還是實現錯了.

搜部落格之後, 發現把int改成long long, 結果得了90分, 最後10分也不知道那裡錯了.

附90分程式碼.

#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
using namespace std; struct Node { long long to, val; bool isb; Node (long long t, long long v, long long i) : to(t), val(v), isb(i) {} }; struct Dis { long long last, dis; // if !big (small), then how long before. }; const long long maxn = 505, INF = 0x3f3f3f3f; vector<Node> G[maxn]; long long n, m, vis[maxn] = {}; Dis dis[maxn][2]; void Dijkstra() { dis[1][0].dis = dis[1][1].dis = 0; for (long long k = 0; k < n; ++k) { long long u = -1, _min = INF; for (long long i = 1; i <= n; ++i) { if (!vis[i] && (dis[i][0].dis < _min || dis[i][1].dis < _min)) { _min = min(dis[i][0].dis, dis[i][1].dis); u = i; } } vis[u] = true; for (long long i = 0, v, w; i < G[u].size(); ++i) { v = G[u][i].to, w = G[u][i].val; if (!G[u][i].isb) { if (w + dis[u][0].dis < dis[v][0].dis) dis[v][0].dis = w + dis[u][0].dis; if (w + dis[u][1].dis < dis[v][0].dis) dis[v][0].dis = w + dis[u][1].dis; } else { if (dis[u][0].dis + w * w < dis[v][1].dis) { dis[v][1].dis = dis[u][0].dis + w * w; dis[v][1].last = w; } if (dis[u][1].last != -1) { long long d = dis[u][1].dis + (dis[u][1].last + w)*(dis[u][1].last + w) - dis[u][1].last*dis[u][1].last; dis[v][1].dis = min(dis[v][1].dis, d); dis[v][1].last = dis[u][1].last + w; } } } } } int main() { for (long long i = 0; i < maxn; ++i) { dis[i][0].dis = dis[i][1].dis = INF; dis[i][0].last = dis[i][1].last = -1; } cin >> n >> m; for (long long i = 0, a, b, c, d; i < m; ++i) { cin >> a >> b >> c >> d; G[b].push_back(Node(c, d, a)); G[c].push_back(Node(b, d, a)); } Dijkstra(); cout << min(dis[n][0].dis, dis[n][1].dis); }