图论刷题计划与题解1(最短路问题)
题目1:P1629 邮递员送信(建反图做两次dijkstra)题目2:P1144 最短路计数题目3: P1828 [USACO3.2]香甜的黄油 Sweet Butter题目4: P1576 最小花费题目5: P5767 [NOI1997] 最优乘车题目6: P5764 [CQOI2005]新年好
图论刷题计划与题解1(最短路问题) 题目1:P1629 邮递员送信(建反图做两次dijkstra)可以先做一遍dijskra,然后对每个点做一遍回来的dijkstra,代码很复杂,而且显然超时,如果考虑建反图只需要做两次dijkstra,因为每个点到起点的距离等价于起点到各个点的距离,而且在反图上,原来的正向边变反向边,反向边变正向边,两次dijkstra都只可能走正向边。(因为dijkstra本来就是走正向边)
需要注意建反图后,n的范围是2倍,所以需要把n的范围开大,不然会re
P1629
#includeusing namespace std; typedef long long ll; const int N = 2e3+10,M = 2e5+10; int h[N],w[M],e[M],ne[M],idx,dist[N]; int n,m; bool st[N]; typedef pair PII; void add(int a, int b, int c) // 添加一条边a->b,边权为c { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ; } void dijkstra(int s) { dist[s] = 0; priority_queue ,greater >heap; heap.push({0,s}); while(heap.size()) { auto t = heap.top(); heap.pop(); int distance = t.first,id = t.second; if(st[id]) continue; st[id] = true; for(int i = h[id];i!=-1;i= ne[i]) { int j = e[i]; if(dist[j]>distance+w[i]) { dist[j] = distance+w[i]; heap.push({dist[j],j}); } } } } int main() { memset(dist,0x3f3f3f3f,sizeof dist); memset(h, -1, sizeof h); memset(st, 0, sizeof st); cin>>n>>m; while(m--) { int a,b,c; cin>>a>>b>>c; add(a, b, c); add(b+n,a+n,c);//小trik +n建反图,但是要 //注意N的范围 } dijkstra(1); ll sum = 0; for(int i = 1;i<=n;i++) { sum+=dist[i]; } memset(st, 0, sizeof st); memset(dist,0x3f3f3f3f,sizeof dist); dijkstra(1+n); for(int i = 2+n;i<=n*2;i++) { sum+=dist[i]; } cout< 题目2:P1144 最短路计数 这个是对于无向无权图的最短路计数,所以可以直接使用bfs遍历即可,看每个点到1的最短距离是否等于它的前个节点的最短距离+1,如果是则计数。最后输出即可。
P1144#include#include #include using namespace std; typedef long long ll; const ll N = 1000000+10,M = 2000000+10; ll mod = 100003; ll h[N], e[M], ne[M], idx,n,m; ll dist[N],f[N],q[N]; bool st[N]; void add(ll a,ll b) {//加边 e[idx] = b;ne[idx] = h[a];h[a] = idx++; } void bfs(ll u) { ll hh = 0,tt = -1; q[++tt] = u; st[u] = true; dist[u] = 0; f[u] = 1; while(hh<=tt) { auto t = q[hh++]; for(ll i = h[t];~i;i = ne[i]) { ll j = e[i]; if(!st[j]) { st[j] = true; dist[j] = dist[t]+1; q[++tt] = j; } if(dist[j]==dist[t]+1) { f[j] = (f[j]+f[t])%mod; } } } } int main() { memset(dist,0x3f3f3f3f,sizeof dist); memset(st, 0, sizeof st); memset(h, -1, sizeof h); memset(f,0,sizeof f); cin>>n>>m; while (m -- ){ ll a,b; cin>>a>>b; add(a,b); add(b,a); } bfs(1); ll ans = 0; for(ll i = 1;i<=n;i++) { cout< 题目3: P1828 [USACO3.2]香甜的黄油 Sweet Butter P1828
以每个牧场为起点,都做一遍dijskra,然后定义全局变量ans,每次把ans与奶牛距离之和取min即可
AC code:#include#include #include #include using namespace std; #define INF 0x3f3f3f3f typedef pair PII; int n,p,c; const int N = 1010,M = 2910; int h[N], e[M], w[M], ne[M], idx; int dist[N]; bool st[N]; int id[N]; int ans = INF; void add(int a,int b,int c) { e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++; } int dijskra(int u) { for(int i = 1;i<=p;i++) { dist[i] = INF; st[i] = false; } dist[u] = 0; priority_queue ,greater >heap; heap.push({0,u});//编号为1,距离为0 while(heap.size()) { auto t = heap.top(); heap.pop(); int distance = t.first,id = t.second; if(st[id]) continue; st[id] = true; for(int i = h[id];~i;i = ne[i]) { int j = e[i]; if(dist[j]>distance+w[i]) { dist[j] = distance+w[i]; heap.push({dist[j],j}); } } } int sum = 0; for(int j = 1;j<=n;j++) { if(dist[id[j]]==INF) return INF; sum+=dist[id[j]]; } return sum; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); memset(h, -1, sizeof h); cin>>n>>p>>c; for(int i = 1;i<=n;i++) cin>>id[i];//每只奶牛所在的牧场号 while(c--) { int a,b,w; cin>>a>>b>>w; add(a,b,w); add(b,a,w); } for(int i = 1;i<=p;i++) { ans = min(ans,dijskra(i)); } cout< 题目4: P1576 最小花费 P1576
dijskra的变种,需要注意如何建图,找到乘积最大的边,然后100/dist[n]即可。#include题目5: P5767 [NOI1997] 最优乘车#include #include #include using namespace std; typedef pair PDI; const int N = 4010,M = 2e5+10; int h[N],e[M],ne[M],idx; bool st[N]; double dist[N]; double w[M]; int n,m; void add(int a,int b,double c) { e[idx] = b,w[idx] = (1-c*0.01),ne[idx] = h[a],h[a] = idx++; } void dijiskra(int start) { dist[start] = 1; priority_queue ,less >heap; heap.push({1,start}); while(heap.size()) { auto t = heap.top(); heap.pop(); int id = t.second; double distance = t.first; if(st[id]) continue; st[id] = true; for(int i = h[id];~i;i = ne[i]) { int j = e[i]; if(dist[j] >n>>m; while (m -- ){ int a,b,c; cin>>a>>b>>c; add(a,b,c); add(b,a,c); } int start,end; cin>>end>>start; dijiskra(start); printf("%.8lf",100/dist[end]); } P5767
需要注意是有向图,且建图方式比较特别,学习stringstream的使用,另外要找到一个性质:换乘次数即为最少坐车次数-1//本来以为是无向图,但是仔细阅读题意后,发现是有向图 //观察到数据范围小,所以可以邻接矩阵建图 #include#include #include #include #include #include using namespace std; const int N = 510,M = 110; int a[N],q[N]; int g[N][N]; int n,m; int dist[N]; void bfs(int u) { memset(dist,0x3f3f3f3f,sizeof dist); int hh = 0,tt = -1; q[++tt] = u; dist[1] = 0; while(hh<=tt) { auto t = q[hh++]; for(int i = 1;i<=n;i++) { if(g[t][i]&&dist[i]>dist[t]+1) { dist[i] = dist[t]+1; q[++tt] = i; } } } } int main() { memset(g,0,sizeof dist); cin>>m>>n; string line; getline(cin,line); while (m--){ //建图 getline(cin,line); stringstream ssin(line); int cnt = 0,p; while(ssin>>p) a[cnt++] = p; for(int j = 0;j 题目6: P5764 [CQOI2005]新年好 P5764
先做dijskra预处理dist数组,注意这里需要开二维的dist数组,然后dfs枚举所有情况即可#includeusing namespace std; const int N = 5e4+10,M = 2e5+10; typedef pair PII; int h[N], e[M], w[M], ne[M], idx; int dist[6][N],n,m; bool st[N]; bool vis[N];//爆搜时用来标记状态 int a[N],ans; #define INF 1e9 void add(int a,int b,int c) { e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++; } void dijkstra(int u,int dist[]) { memset(dist,0x3f3f3f3f,4*N);//只memset一维' memset(st, 0, sizeof st); dist[u] = 0; priority_queue ,greater >heap; heap.push({0,u}); while(heap.size()) { auto t = heap.top(); heap.pop(); int id = t.second,distance = t.first; if(st[id]) continue; st[id] = true; for(int i = h[id];~i;i = ne[i]) { int j = e[i]; if(dist[j]>distance+w[i]) { dist[j] = distance+w[i]; heap.push({dist[j],j}); } } } } int dfs(int u,int start,int distance) { if(u==6) { return distance; } int res = INF; for(int i = 1;i<=5;i++) { if(!vis[i]) { int next = a[i]; vis[i] = true; res = min(res,dfs(u+1,i,distance+dist[start][next])); vis[i] = false; } } return res; } int main() { memset(h, -1, sizeof h); cin>>n>>m; a[0] = 1; for(int i = 1;i<=5;i++) { cin>>a[i]; } while (m -- ){ int a,b,c; cin>>a>>b>>c; add(a, b, c); add(b, a, c); } for(int i = 0;i<6;i++) dijkstra(a[i],dist[i]);//先做dijskra预处理dist数组 cout<



