今日打卡不是题目名,是真的打卡,写了一天的代码有些小累,总结总结,顺便休息休息。
为了应对CCF考试的第四题,本人开始干图论,由于基础一般,目前做的都是模板题,大佬可以绕行聊~
来一道题luogu社交网络:这里还涉及到计算最短路径数量的一个方法。用一个times[u][v]数组存储u和v之间最短路的数量,初始化都是1(题目说明任意两点都相连时)。每次能更新最短路u-v时,(中间节点为k),times[u][v]也要相应更新为u-k间最短路数 * k-v间最短路数,这个就是分步联系事件的乘法原理。当u-v间距离恰好==u-k距离与k-v距离之和时,不同情况的事件加法原理增加u-v之间的最短路数量。
这道题还涉及精度的问题,用float会丢失精度,必须用double。这个原因我还没弄懂,希望能有人解答,我个人认为是因为是long long两个数相除的原因。一般严格小于6位的精度,可以直接用float;小于16位的精度可以使用double。题目:社交网络
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define Inf 0x3f3f3f3f #define ll long long const int maxv = 100 + 4, maxe = 4500 + 4; int table[maxv][maxv]; ll edge[maxv][maxv]; double Imp[maxv]; int n, m, u, v, w; //注意floyd是所有点间的遍历故双向性已经考虑在内 //每次单向处理即可 void floyd(){ for(int k=1; k<=n; ++k){ for(int i=1; i<=n; ++i){ for(int j=1; j<=n; ++j){ if(table[i][j] > table[i][k] + table[k][j]){//重新更新 table[i][j] = table[i][k] + table[k][j]; edge[i][j] = edge[i][k] * edge[k][j]; } else if(table[i][j] == table[i][k] + table[k][j]){ //不需要更新,因为中转节点是变化的,所以必然是新的最短路,累加即可 //不用担心有重复的计算。乘法原理 edge[i][j] += edge[i][k] * edge[k][j]; } } } } return; } void solve(){ for(int k=1; k<=n; ++k){ for(int i=1; i<=n; ++i){ for(int j=1; j<=n; ++j){ if(i==j || j==k || i==j) continue; if(table[i][j] == table[i][k] + table[k][j]) Imp[k] += ((double)edge[i][k] * (double)edge[k][j] / (double)edge[i][j]); } } printf("%.3f\n", Imp[k]); } return; } inline int read(){ char ch = getchar(); int ans = 0; while(ch < ‘0‘ || ch >‘9‘) ch = getchar(); while(ch >= ‘0‘ && ch <= ‘9‘){ ans = (ans << 3) + (ans << 1) + ch - ‘0‘; ch = getchar(); }return ans; } void inline ini() { memset(table, Inf, sizeof(table)); n = read(), m = read(); while(m--){ u = read(), v = read(), w = read(); table[u][v] = table[v][u] = w; edge[u][v] = edge[v][u] = 1; } return; } int main() { ini(); floyd(); solve(); return 0; }
还有一道,这道题稍微要思考一下,要事先对中间节点排序再枚举。newid[]数组好好意会意会,有点像数学中的坐标转换~将所有变量在我理想的“坐标下实现”, 最后的输出也要是在这个“理想坐标下”操作。感觉题目有些表述问题,一开始一直以为是加上所有节点后的最大值。题目:牛收费
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define Inf 0x3f3f3f3f #define ll long long const int maxn = 250 + 4; struct Node{ int w, id; }node[maxn]; ll table[maxn][maxn], dis[maxn][maxn]; int n, m, k, u, v, w, st, en; int newid[maxn]; inline int read(){ char ch = getchar(); int ans = 0; while(ch < ‘0‘ || ch > ‘9‘) ch = getchar(); while(ch >= ‘0‘ && ch <= ‘9‘){ ans = (ans << 3) + (ans << 1) + ch - ‘0‘; ch =getchar(); }return ans; } inline bool comp(const Node& a, const Node& b){ return a.w < b.w; } inline void floyd(){ for(int k=1; k<=n; ++k){ for(int i=1; i<=n; ++i){ for(int j=1; j<=n; ++j){ if(i == j || i == k || j == k) continue; table[i][j] = min(table[i][j], table[i][k] + table[k][j]); dis[i][j] = min(dis[i][j], table[i][j] + max(node[k].w, max(node[i].w, node[j].w))); } } } return; } //涉及到最短路和最短路上的最大节点,用两个数组分别存储 //由于floyd中间节点由前往后遍历,所以我们把权值越大的点越往后遍历 //使得最终遍历到k==n时,保证如果选取中间节点选取的节点是所有中间节点中权值最大的 int main() { memset(table, Inf, sizeof(table)); memset(dis, Inf, sizeof(dis)); n = read(), m = read(), k = read(); for(int i=1; i<=n; ++i) {node[i].w = read(), node[i].id = i, table[i][i] = 0;} sort(node+1, node+n+1, comp); //节点按权值升序 for(int i=1; i<=n; ++i) newid[node[i].id] = i; //相当于把我想要优先遍历的序号排在前边 //这样在floyd中的遍历就更加方便注意之后的存图位置也要用newid数组 for(int i=1; i<=m; ++i){ u = read(), v = read(), w = read(); table[newid[u]][newid[v]] = table[newid[v]][newid[u]] = min(table[newid[u]][newid[v]], (ll)w); //为方便之后的floyd访问,用newid【】数组来获取新的顺序编号 //转换之后,权值较小的节点会被优先遍历到 //注意可能会有重边,所以要有一个min()判断一下 } floyd(); while(k--){ st = read(), en = read(); printf("%lld\n", dis[newid[st]][newid[en]]); } return 0; }
#include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; #define ll long long #define Inf 0x3f3f3f3f const int maxe = 50000 + 5, maxv = 50000 + 5; struct Edge{ int to, w; int next; Edge(int a=0, int b=0, int c=0):to(a), w(b), next(c){} }e[maxe * 2]; int head[maxv], cnte, pw[maxv]; struct Node{ int u; ll dis; Node(int a=0, ll b=0):u(a), dis(b){} bool operator < (const Node& b) const{ return this->dis > b.dis; } }; bool vis[maxv]; ll ans, dis[maxv]; int nv, ne, a, b, w, st, t; inline void ini() { memset(head, 0, sizeof(head)); cnte = ans = 0; } inline void add_edge(const int& from, const int& to, const int& w){ ++cnte; e[cnte].to = to, e[cnte].w = w; e[cnte].next = head[from]; head[from] = cnte; return; } //与Prim的区别,入队更新的是到源点的距离dis[cur.u] + e[i].w //Prim入队更新的的是到新生成的树的距离e[i].w //已经确定过的点就不要再入队了,减少stl中不必要的排序 void dijkstra(){ memset(vis, false, sizeof(vis)); for(int i=1; i<=nv; ++i) dis[i] = Inf; //注意这里的Inf不是0x3f3f3f3f只能用循环赋值 dis[st] = 0; priority_queue<Node> pq; while(pq.size()) pq.pop(); pq.push(Node(st, 0)); while(pq.size()){ Node cur = pq.top(); pq.pop(); if(vis[cur.u]) continue; vis[cur.u] = true; for(int i = head[cur.u]; i; i = e[i].next) { if(vis[e[i].to]) continue; if(dis[e[i].to] > dis[cur.u] + e[i].w){ dis[e[i].to] = dis[cur.u] + e[i].w; pq.push(Node(e[i].to, dis[e[i].to])); //无需再判断是否已经确定,直接入队即可,因为每次弹出的值都是经过筛选的最小值 //且访问过的不会再操作 } else continue; } } return; } inline int read(){ char ch = getchar(); int ans = 0; while(ch < ‘0‘ || ch > ‘9‘) ch = getchar(); while(ch >= ‘0‘ && ch <= ‘9‘){ ans = (ans << 3) + (ans << 1) + ch - ‘0‘; ch = getchar(); }return ans; } int main() { t = read(); while(t--){ ini(); nv = read(), ne = read(), st = 1; for(int i=1; i<=nv; ++i) pw[i] = read(); for(int i=0; i<ne; ++i){ a = read(), b = read(), w = read(); add_edge(a, b, w); add_edge(b, a, w); } dijkstra(); bool have_ans = true; for(int i=1; i<=nv; ++i){ if(dis[i] == Inf){ have_ans = false; break; } ans += dis[i] * pw[i]; } if(have_ans) printf("%lld\n", ans); else puts("No Answer"); } return 0; }
这里仍然来一道题目。这题中比较巧秒的是建立正反图,将问题都转化为单源最短路,然后就可以用dijkstra了。题目:邮递员
具体的题目不记得了,贴个板子:
#include <iostream> #include <queue> #include <cstring> using namespace std; #define ll long long #define Inf 0x3f3f3f3f const int maxn = 50000 + 5; struct Edge{ int to, w; int next; Edge(int a=0, int b=0, int c=0):to(a), w(b), next(c){} }e[maxn * 2]; int st, cnte, head[maxn], pw[maxn]; bool in_que[maxn]; ll ans, dis[maxn]; int nv, ne, u, v, w, t; inline void ini() { memset(in_que, false, sizeof(in_que)); memset(dis, Inf, sizeof(dis)); memset(head, 0, sizeof(head)); cnte = ans = 0; return; } inline void add_edge(const int& from, const int& to, const int& w) { ++cnte; e[cnte].to = to, e[cnte].w = w; e[cnte].next = head[from]; head[from] = cnte; return; } //单源最短路 void spfa(const int s){ queue<int> que; while(que.size()) que.pop(); que.push(s); in_que[s] = true; dis[s] = 0; while(que.size()){ int cur = que.front(); que.pop(); in_que[cur] = false; for(int i = head[cur]; i; i = e[i].next){ if(dis[e[i].to] > dis[cur] + e[i].w){ dis[e[i].to] = dis[cur] + e[i].w; if(!in_que[e[i].to]){ que.push(e[i].to); in_que[e[i].to] = true; } } } } return; }
原文:https://www.cnblogs.com/GorgeousBankarian/p/11154011.html