\(n\) 個點 \(m\) 條邊的無向連通圖,最多能夠將 \(k\) 條邊的權值賦爲 \(0\).求 \(S\) 到 \(t\) 的最短路徑ios
分層圖跑最短路,圖與圖之間的用權值爲 \(0\) 的邊鏈接,每下一層表明免費一次,只需求 \(S\) 到 \(t+k*n\)的最短路便可c++
#include<cstdio> #include<cctype> #include<cstring> #include<queue> #include<algorithm> #include<vector> #include<utility> #include<functional> int Read() { int x=0;char c=getchar(); while(!isdigit(c)) { c=getchar(); } while(isdigit(c)) { x=x*10+(c^48); c=getchar(); } return x; } using std::priority_queue; using std::pair; using std::vector; using std::make_pair; using std::greater; struct Edge { int to,next,cost; }edge[2500001]; int cnt,head[110005]; void add_edge(int u,int v,int c=0) { edge[++cnt]=(Edge){v,head[u],c}; head[u]=cnt; } int dis[110005]; bool vis[110005]; void Dijkstra(int s) { memset(dis,0x3f,sizeof(dis)); dis[s]=0; priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > points; points.push(make_pair(0,s)); while(!points.empty()) { int u=points.top().second; points.pop(); if(!vis[u]) { vis[u]=1; for(int i=head[u];i;i=edge[i].next) { int to=edge[i].to; if(dis[to]>dis[u]+edge[i].cost) { dis[to]=dis[u]+edge[i].cost; points.push(make_pair(dis[to],to)); } } } } } int main() { int n=Read(),m=Read(),k=Read(),s=Read(),t=Read(); int u,v,c; for(int i=0;i<m;++i) { u=Read(),v=Read(),c=Read(); add_edge(u,v,c); add_edge(v,u,c); for(int j=1;j<=k;++j) { add_edge(u+(j-1)*n,v+j*n); add_edge(v+(j-1)*n,u+j*n); add_edge(u+j*n,v+j*n,c); add_edge(v+j*n,u+j*n,c); } } //坑人的數據=================================== for(int i=1;i<=k;++i){ add_edge(t+(i-1)*n,t+i*n); } //=========================================== Dijkstra(s); printf("%d",dis[t+k*n]); return 0; }
在加權無向圖上求出一條從 \(1\) 號結點到 \(N\) 號結點的路徑,使路徑上第 \(K+1\) 大的邊權儘可能小。git
二分求最短路,枚舉第 \(K+1\) 大的邊的長度,若是比枚舉的值大,邊權就爲 \(1\),不然爲 \(0\) ,而後跑最短路,若是最短路徑大於 \(k\) ,那就合法,繼續縮小範圍,不然擴大範圍算法
/* work by:Ariel_ */ #include <iostream> #include <cstdio> #include <vector> #include <queue> #include <cstring> #include <utility> const int M = 2e3 + 5; int read(){ int x = 0,f = 1;char c = getchar(); while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();} while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();} return f*x; } using std::vector; using std::pair; using std::make_pair; using std::priority_queue; using std::greater; int n, p, k; struct edge{ int v,nxt,w; }e[M << 1]; int cnt,head[M]; void add_edge(int u,int v,int w){ e[++cnt] = (edge){v, head[u], w}; head[u] = cnt; } int dis[M]; bool vis[M]; bool dij(int s,int mid){ memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[1] = 0; priority_queue<pair<int,int> ,vector<pair<int, int> >,greater<pair<int ,int> > >q; q.push(make_pair(0, s)); while(!q.empty()){ int u = q.top().second; q.pop(); if(!vis[u]){ vis[u] = 1; for(int i = head[u]; i;i = e[i].nxt){ int v = e[i].v; if(dis[v] > dis[u] + (e[i].w > mid)){ dis[v] = dis[u] + (e[i].w > mid); q.push(make_pair(dis[v], v)); } } } } if(dis[n] <= k) return true; else return false ; } int main(){ n = read(),p = read(),k = read(); for(int i = 1, u, v, w;i <= p; i++){ u = read(),v = read(),w = read(); add_edge(u, v, w),add_edge(v, u, w); } int l = 0,r = 1000001,ans = 2100000000; while(l <= r){ int mid = (l + r) >> 1; if (dij(1, mid)){ ans = mid; r = mid - 1; } else l = mid + 1; } if(ans == 2100000000)printf("-1"); else printf("%d",ans); }
\(n\) 個點 \(m\) 條邊的有向圖,求\(max(min(i~ -> ~x) + min(x ~->~ i))\);數組
正反圖兩邊最短路優化
/* work by:Ariel_ */ #include <iostream> #include <cstdio> #include <vector> #include <cstring> #include <utility> #include <queue> const int M = 1e5 + 5; const int N = 1e3 + 2; int read(){ int x = 0,f = 1;char c = getchar(); while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();} while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();} return f*x; } using std::vector; using std::priority_queue; using std::make_pair; using std::pair; using std::greater; using std::max; struct edge{ int v,nxt,w; }e[M << 1]; int cnt,head[N]; void add_edge(int u,int v,int w){ e[++cnt] = (edge){v,head[u], w}; head[u] = cnt; } int dis[N],n, m, x; bool vis[N]; void dij(int s){ memset(vis,0,sizeof(vis)); memset(dis,0x3f,sizeof(dis)); dis[s] = 0; priority_queue<pair<int ,int>,vector<pair<int , int> >,greater<pair<int ,int > > >q; q.push(make_pair(0, s)); while(!q.empty()){ int u = q.top().second; q.pop(); if(!vis[u]){ vis[u] = 1; for(int i = head[u]; i;i = e[i].nxt){ int v = e[i].v; if(dis[v] > dis[u] + e[i].w){ dis[v] = dis[u] + e[i].w; q.push(make_pair(dis[v],v)); } } } } } int main(){ n = read(),m = read(),x = read(); for(int i = 1,u, v, w;i <= m; i++){ u = read(),v = read(),w = read(); add_edge(u, v, w); } int tot[N]; for(int i = 1;i <= n; i++){ dij(i); tot[i] = dis[x]; } dij(x); int ans = 0; for(int i = 1;i <= n; i++){ ans = max(ans,tot[i] + dis[i]); } printf("%d",ans); }
給一張無向圖,求這張圖的嚴格次短路之長。spa
兩個 \(dis\) 數組一個存最小值,一個存次小值,維護次小值比最小值大且比其餘的邊的值小設計
/* work by:Ariel_ */ #include <iostream> #include <cstdio> #include <queue> #include <vector> #include <utility> #include <cstring> const int M = 1e5 + 5; const int N = 5e3 + 4; int read(){ int x = 0,f = 1;char c = getchar(); while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();} while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();} return f*x; } using std::queue; int n, m; struct edge{ int v,nxt,w; }e[M << 1]; int cnt,head[M]; void add_edge(int u,int v,int w){ e[++cnt] = (edge){v, head[u], w}; head[u] = cnt; } int dis1[N],opt[N],dis2[N]; bool vis[N]; void spfa(int s){ memset(dis1,0x3f,sizeof(dis1)); memset(dis2,0x3f,sizeof(dis2)); dis1[s] = 0; queue <int> q; q.push(s); while(!q.empty()){ int u = q.front(); q.pop();vis[u] = 0; for(int i = head[u]; i;i = e[i].nxt){ int v = e[i].v; if(dis1[v] > dis1[u] + e[i].w){ dis2[v] = dis1[v]; dis1[v] = dis1[u]+e[i].w; if(!vis[v]) q.push(v),vis[v] = 1; } else if(dis1[u] + e[i].w < dis2[v] && dis1[u] + e[i].w > dis1[v]){ dis2[v] = dis1[u] + e[i].w; if(!vis[v]) q.push(v),vis[v] = 1; } if(dis2[u] + e[i].w < dis2[v]) { dis2[v] = dis2[u] + e[i].w; if(!vis[v]) q.push(v),vis[v] = 1; } } } } int main(){ n = read(),m = read(); for(int i = 1,u,v,w;i <= m; i++){ u = read(),v = read(),w = read(); add_edge(u, v, w),add_edge(v, u, w); } spfa(1); printf("%d", dis2[n]); }
給出一個 \(N\) 個頂點 \(M\) 條邊的無向無權圖,頂點編號爲 \(1 - N\) 問從頂點 \(1\) 開始,到其餘每一個點的最短路有幾條code
跑最短路的時候順便計數,若是\(dis[v] > dis[u] + 1\) 就從新更新 \(ans[v]\) 的值用 \(ans[u]\) 覆蓋排序
若是\(dis[v] == dis[u] + 1\) 就直接 \(ans[v] += dis[u]\) 就行了
/* work by:Ariel_ */ #include <iostream> #include <cstdio> #include <cstring> #include <queue> const int mod = 100003; const int N = 1e6 + 5; const int M = 2e6 + 5; int read(){ int x = 0,f = 1;char c = getchar(); while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();} while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();} return f*x; } using std::queue; struct edge{ int v,nxt; }e[M << 1]; int cnt,head[N]; void add_edge(int u,int v){ e[++cnt] = (edge){v,head[u]}; head[u] = cnt; } int dis[N],ans[N]; bool vis[N]; void spfa(int x){ memset(dis, 0x3f,sizeof(dis)); queue <int> q; q.push(x); dis[1] = 0; vis[1] = 1; ans[1] = 1; while(!q.empty()){ int u = q.front(); q.pop();vis[u] = 0; for(int i = head[u]; i;i = e[i].nxt){ int v = e[i].v; if(dis[v] > dis[u] + 1){ dis[v] = dis[u] + 1; ans[v] = ans[u]; if(!vis[v]){ vis[v] = 1;q.push(v); } } else if(dis[v] == dis[u] + 1){ ans[v] += ans[u]; ans[v] %= mod; } } } } int n,m; int main(){ n = read(),m = read(); for(int i = 1,u, v;i <= m; i++){ u = read(),v = read(); add_edge(u,v);add_edge(v,u); } spfa(1); for(int i = 1;i <= n; i++){ printf("%d\n",ans[i]); } }
無向圖,求 \(1\)號點到其他五個點路徑和的最小值
很簡單,無向圖,分別跑出每一個點的最短路,全排列暴力便可
由於 \(0x3f\) 卡了半上午……
/* work by:Ariel_ */ #include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <queue> #include <cstring> #include <vector> const int N = 5e4 + 5; const int M = 1e5 + 5; int read(){ int x = 0,f = 1;char c = getchar(); while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();} while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();} return f*x; } using std::vector; using std::pair; using std::make_pair; using std::greater; using std::priority_queue; using std::min; using std::cout; int a[7], n, m; struct edge{ int v,nxt,w; }e[M << 1]; int head[N],cnt; void add_edge(int u,int v,int w){ e[++cnt] = (edge){v, head[u], w}; head[u] = cnt; } int dis[N][6]; bool vis[N]; void dij(int x,int num){ memset(vis , 0, sizeof(vis)); for(int i = 1;i <= n; i++) dis[i][num] = 0x3f3f3f3f; priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q; dis[x][num] = 0; q.push(make_pair(0, x)); while(!q.empty()){ int u = q.top().second; q.pop(); if(vis[u])continue; vis[u] = 1; for(int i = head[u]; i; i = e[i].nxt){ int v = e[i].v; if(dis[v][num] > dis[u][num] + e[i].w){ dis[v][num] = dis[u][num] + e[i].w; q.push(make_pair(dis[v][num], v)); } } } } int js,bok[7], ans = 0x3f3f3f3f; void dfs(int x,int tot,int last){ if(x == 5){ ans = min(ans, tot); return ; } for(int i = 1;i <= 5; i++){ if(bok[i]) continue; bok[i] = 1; dfs(x + 1,tot + dis[a[i]][last + 1], i); bok[i] = 0; } } int main(){ n = read(),m = read(); for(int i = 1;i <= 5; i++) a[i] = read(); for(int i = 1,u, v, w;i <= m; i++){ u = read(),v = read(),w = read(); add_edge(u, v, w),add_edge(v, u, w); } dij(1, 1); for(int i = 1;i <= 5; i++) dij(a[i], i + 1); dfs(0, 0, 0); printf("%d\n",ans); }
求一條點和邊均可重複的路徑,使得路徑上存在兩點 \(a, b\) ,使得 \(a\) 先比 \(b\) 訪問且 \(Vb-Va\) 儘量地大
分層圖最短路,建三層圖,在每層圖上跑表明沒有交易,若是從一層圖上了第二層圖表明買入,從第二層上到第三層表明賣出,一二層圖之間用兩點間權值鏈接,二三層圖用負權值鏈接,跑出最短路,貿易總額就是最短路的相反數
/* work by:Ariel_ */ #include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <vector> #include <queue> const int N = 1e5 + 5; const int M = 5e5 + 5; int read(){ int x = 0,f = 1;char c = getchar(); while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();} while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();} return f*x; } using std::priority_queue; using std::vector; using std::pair; using std::make_pair; using std::greater; using std::max; struct edge{ int v,nxt,w; }e[M * 3]; int cnt,head[N * 3]; void add_edge(int u,int v,int w){ e[++cnt] = (edge){v,head[u], w}; head[u] = cnt; } bool vis[M * 3]; int dis[M * 3]; void dij(int x){ memset(dis, 0x3f3f3f, sizeof(dis)); priority_queue<pair<int ,int>,vector<pair<int , int> >,greater<pair<int ,int> > > q; q.push(make_pair(0, x)); dis[x] = 0; while(!q.empty()){ int u = q.top().second;q.pop(); for(int i = head[u];i;i = e[i].nxt){ int v = e[i].v; if(dis[v] > dis[u] + e[i].w){ dis[v] = dis[u] + e[i].w; q.push(make_pair(dis[v],v)); } } } } int n,m,a[N]; int main(){ n = read(),m = read(); for(int i = 1;i <= n; i++) a[i] = read(); for(int i = 1,u, v,flag;i <= m; i++){ u = read(),v = read(),flag = read(); if(flag == 1){ for(int k = 0;k < 3; k++) add_edge(u + k * n , v + k * n, 0); add_edge(u, v + n, a[v]); add_edge(u + n,v + n + n, -a[v]);//賣出去 } else{ for(int k = 0;k < 3; k++){ add_edge(u + k * n,v + k * n, 0); add_edge(v + k * n,u + k * n, 0); } add_edge(u, v + n, a[v]); add_edge(v, u + n, a[u]); add_edge(u + n,v + n * 2, -a[v]); add_edge(v + n,u + n * 2, -a[u]); } add_edge(n, 3 * n, 0); } dij(1); int ans = 0; ans = max(ans,-dis[n*3]); printf("%d",ans); }
給你一個\(N*N\)的矩陣,求汽車從\((1,1)\)到\((N,N)\)的最小花費,汽車行進一格須要消耗一點油,汽車油儲量爲 \(k\).
1.若汽車到達一個有加油站的點,那麼必須將油加滿爲 \(k\),花費爲 \(a\);
2.若汽車往回走,即前往的點的橫座標或者縱座標在減少,須要花費 \(b\);
3.汽車也能夠本身新建一個加油站,花費\(c\) (不包括加油費用 \(a\) )
分層圖,最短路;\(k\) 的範圍很小,咱們能夠建 \(k + 1\) 層圖跑最短路,具體就是以上三種狀況
注意特殊狀況:剛好到終點,剛好沒有,此時不須要加油,因此須要打個特判
#include <iostream> #include <cstdio> #include <cstring> #include <queue> #include <vector> using namespace std; const int N = 101; const int dx[4] = {0, 1, 0, -1}; const int dy[4] = {1, 0, -1, 0}; int read(){ int x = 0,f = 1;char c = getchar(); while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();} while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();} return f*x; } int n,k,a,b,c; int mp[N][N],dis[N][N][11]; bool vis[N][N][11]; priority_queue<pair<int,pair<int,pair<int ,int> > > >q; void dij(){ memset(dis, 0x3f3f3f3f, sizeof(dis)); dis[1][1][k] = 0; q.push(make_pair(0,make_pair(k,make_pair(1, 1)))); while(!q.empty()){ int z = q.top().second.first,x = q.top().second.second.first,y = q.top().second.second.second;q.pop();//x,y爲座標,z爲油量 if(!vis[x][y][z]){ vis[x][y][z] = 1; for(int i = 0;i < 4; i++){ int nx =x + dx[i],ny = y + dy[i],nz = z - 1,cost = dis[x][y][z]; if(nx > 0&&nx <= n&&ny > 0&&ny <= n){ if(mp[nx][ny] == 1){//強制消費 cost += a;nz = k; } if(!nz)//沒有油了也沒碰見加油站就建一個 cost += c + a,nz = k; if(i >= 2)//向後走 cost += b; if(cost < dis[nx][ny][nz]){ dis[nx][ny][nz] = cost; q.push(make_pair(-cost,make_pair(nz,make_pair(nx, ny)))); } } } } } int ans = 0x3f3f3f3f; if(dis[n][n][k] != 0x3f3f3f3f) dis[n][n][0] = dis[n][n][k] - a - c;//走到終點剛好沒有,特判 for(int i = 0;i < k;i++){ ans = min(ans,dis[n][n][i]); } printf("%d",ans); } int main(){ n = read(),k = read(),a = read(),b = read(),c = read(); for(int i = 1;i <= n; i++){ for(int j = 1;j <= n; j++){ mp[i][j] = read(); } } dij(); }
單元最短路,有負邊,無負環,部分雙向邊,部分無向邊(spfa會卡)
1.spfa,SLF 優化: 雙向隊列
/* work by:Ariel_ */ #include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <queue> #include <stack> #include<climits> const int N = 1e5 + 5; using std::deque; deque <int> q; int read(){ int x = 0,f = 1;char c = getchar(); while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();} while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();} return f*x; } struct edge{ int v,nxt,w; }e[N << 1]; int cnt,head[N]; void add_edge(int u,int v,int w){ e[++cnt] = (edge){v, head[u], w}; head[u] = cnt; } int n, r, p, s, dis[N]; bool vis[N]; void spfa(int s){ memset(dis,0x3f3f3f3f, sizeof(dis)); dis[s] = 0;vis[s] = 1; q.push_back(s); while(!q.empty()){ int u = q.front();q.pop_front(); for(int i = head[u]; i; i = e[i].nxt){ int v = e[i].v; if(dis[v] > dis[u] + e[i].w){ dis[v] = dis[u] + e[i].w; if(!vis[v]){ if(!q.empty() && dis[v] >= dis[q.front()])q.push_back(v); else q.push_front(v); vis[v] = 1; } } } vis[u] = 0; } } int main(){ n = read(),r = read(),p = read(),s = read(); for(int i = 1,u, v, w;i <= r; i++){ u = read(),v = read(),w = read(); add_edge(u, v, w),add_edge(v, u, w); } for(int i = 1,u, v, w;i <= p; i++){ u = read(),v = read(),w = read(),add_edge(u, v, w); } spfa(s); for(int i = 1;i <= n; i++){ if(dis[i] != 0x3f3f3f3f)printf("%d\n",dis[i]); else printf("NO PATH\n"); } }
設 \(G\) 爲有 \(n\) 個頂點的帶權有向無環圖,\(G\) 中各頂點的編號爲 \(1\) 到 \(n\),請設計算法,計算圖 \(G\) 中 \(<1,n>\) 間的最長路徑。
由於到達n點的值只能用從 \(1\) 出發的路徑更新,因此直接拓撲排序,求最長路便可
/* work by:Ariel_ */ #include <iostream> #include <cstdio> #include <queue> #include <cstring> using namespace std; const int M = 500500; int read(){ int x = 0,f = 1;char c = getchar(); while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();} while(c >= '0'&&c <= '9'){x = x*10 + c - '0';c = getchar();} return f*x; } int ru[M]; struct edge{ int v, nxt, w; }e[M << 1]; int cnt,head[M]; void add_edge(int u,int v,int w){ e[++cnt] = (edge){v, head[u], w}; head[u] = cnt; } int n, m,vis[M],dis[M]; void topu(){ queue<int> q; for(int i = 1;i <= n; i++) if(!ru[i])q.push(i); dis[n] = -1;vis[1] = 1; while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i;i = e[i].nxt){ int v = e[i].v; ru[v]--; if(vis[u] == 1){ if(dis[v] < dis[u] + e[i].w) dis[v] = dis[u] + e[i].w; vis[v] = 1; } if(!ru[v]) q.push(v); } } } int main(){ n = read(),m = read(); for(int i = 1,u, v, w;i <= m; i++){ u = read(),v = read(),w = read(); ru[v]++; add_edge(u, v, w); } topu(); printf("%d",dis[n]); }