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);
}