最短路問題(spfa)
阿新 • • 發佈:2018-03-17
i++ 最短路 scanf out 圖片 格式 http 當前 一次
自己是真的笨
整整用了10個小時才吃透這個BF的兩種優化
題目如下:
問題描述 給定一個n個頂點,m條邊的有向圖(其中某些邊權可能為負,但保證沒有負環)。請你計算從1號點到其他點的最短路(頂點從1到n編號)。 輸入格式 第一行兩個整數n, m。 接下來的m行,每行有三個整數u, v, l,表示u到v有一條長度為l的邊。 輸出格式 共n-1行,第i行表示1號點到i+1號點的最短路。 樣例輸入 3 3 1 2 -1 2 3 -1 3 1 2 樣例輸出 -1 -2 數據規模與約定 對於10%的數據,n = 2,m = 2。 對於30%的數據,n <= 5,m <= 10。 對於100%的數據,1<= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保證從任意頂點都能到達其他所有頂點。
很簡單的題,就是測試數據n居然有20000;
那麽需要考慮的問題就有兩個了,一是時間復雜度,二是空間復雜度
一開始上手我用的是floyd
代碼如下:
#include <iostream> #define max 2001 #define inf 99999 using namespace std; int a[max][max]={0}; //鄰接矩陣儲存圖的信息 int main() { int m,n,u,v,l; cin>>n>>m;for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) a[i][j]=inf; for(int i=1;i<=m;i++) { cin>>u>>v>>l; a[u][v]=l; } for(int k=1;k<=n;k++) //floyd主體 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)if(a[i][j]>a[i][k]+a[k][j]) a[i][j]=a[i][k]+a[k][j]; for(int i=2;i<=n;i++) cout<<a[1][i]<<endl; return 0; }
過了50%的數據,意料之中,兩個問題都犯了
o(n^3)的時間復雜度 20000是不可能的
二維數組開到20000也是不可能的,內存超出限制,oj顯示編譯錯誤
然後我用了BF,用了一個結構體數組來儲存點與點之間的路徑
class road{ public: int start; //起始點 int target; //終點 int distance;//距離 }; road roadque[max];
n個點需要n-1遍 每遍枚舉每一條邊 時間復雜度o(nm)
for(int k=1;k<=n-1;k++) //進行n-1次松弛 for(int i=1;i<=m;i++) //枚舉每一條邊 if(dis[roadque[i].target]>dis[roadque[i].start]+dis[roadque[i].distance])//嘗試松弛每一條邊 dis[roadque[i].target]=dis[roadque[i].start]+dis[roadque[i].distance];
按題中的數據的話是40億次運算,emmmmm,TLE,GG
然後我去學了SPFA
隊列優化了一遍,代碼如下:
#include <iostream> #include <cstdio> #include <cstring> #include <queue> using namespace std; const int N = 20001; const int INF = 99999999;//理解成正無窮 int map[N][N], dist[N]; //map鄰接矩陣,dis當前點到其他點的距離數組 bool book[N]; //判斷是否已經在隊列中 int n, m; void init() {//初始化 int i, j; for (i = 1; i <= N; i++) for (j = 1; j <= N; j++) { if (i == j) map[i][j] = 0; else map[i][j] = INF; } } void spfa(int start) { queue<int> Q; int i, now; memset(book, false, sizeof(book)); for (i = 1; i <= n; i++) dist[i] = INF; dist[start] = 0; //到自己的距離為0 Q.push(start); //把自己加入到隊列中 book[start] = true;//不可以入隊 while (!Q.empty()) { now = Q.front(); Q.pop(); book[now] = false; for (i = 1; i <= n; i++) if (dist[i] > dist[now] + map[now][i]) { dist[i] = dist[now] + map[now][i]; if (book[i] == 0) { Q.push(i); //如果松弛成功且當前點不在隊列中,則加入到隊列中 book[i] = true;//置為不可以入隊狀態 } } } } int main() { scanf("%d%d", &n, &m); init(); for (int i = 0; i < m; i++) { int a, b, c; scanf("%d%d%d", &a, &b, &c); if (map[a][b] > c) map[a][b] = c; } spfa(1); for (int i = 1; i <= n; i++) cout << dist[i] << endl; return 0; }
時間復雜度看西交大的論文裏說大概是o(km) k一般為1-2
emmmmmm,1-2嗎?我信了你的邪,這個圖m有20w
平均一下20w/2w 差不多每個點進去10次吧 k≈10
吐血 不過還好,不是最壞的時間復雜度
嗯,那麽到這裏超時的問題就解決了
但是!
鄰接矩陣要開到2w明顯不可能
所以又一次編譯錯誤,
然後我去學了鄰接表
數組方法的鄰接表太巧妙了,老實人用不了,溜了
這裏使用stl裏的list來實現鄰接表
對於這樣的數據
4 5
1 4 9
4 3 8
1 2 5
2 4 6
1 3 7
這是示意圖
#include <iostream> #include <list> #include <queue> #include <cstring> #define max 20001 const int inf = 1<<29; int sfpa(int v0); using namespace std; class road { public: int target; int distance; }; list<road> roadlist[max]; //鄰接表 int dis[max]; //距離數組 int n, m; int main() { cin >> n >> m; int a, b, c; for (int i = 0; i<m; i++)//錄入鄰接表 { cin >> a >> b >> c; road ex; ex.target = b; ex.distance = c; roadlist[a].push_front(ex); } sfpa(1); for (int i = 2; i <= n; i++) cout << dis[i] << endl; return 0; } int sfpa(int v0) { queue<int> que; int book[max]; //判斷是否在隊列中 memset(book, 0, sizeof(book)); for (int i = 1; i <= n; i++)//初始化 dis[i] = inf; dis[v0] = 0; que.push(v0); book[v0] = 1; while (!que.empty()) { int now = que.front(); que.pop(); book[now] = 0; list<road>::iterator it = roadlist[now].begin(); for (it; it != roadlist[now].end(); it++)//遍歷領接表 { if (dis[it->target] > dis[now] + (it->distance)) { dis[it->target] = dis[now] + (it->distance);//更新距離 if (book[it->target] == 0)//如果松弛成功且不在隊列中,則把該點加入隊列 { que.push(it->target); book[it->target] = 1; } } } } return 0; }
需要註意的是,只有松弛成功才允許把點加到隊列中
到了這裏終於ac了
= =呼
不知道為啥,用spfa總覺得不安心
dijkstra又解決不了負權邊
希望藍橋杯沒有卡spfa的題
卡spfa真的喪心病狂!
最短路問題(spfa)