清北学堂(2019 5 2) part 5
今天讲图论,顺便搞一搞之前没弄完的前向星dij
1.图的基本概念(课件原话):
G (图)= (V(点); E(边))
一般来说,图的存储难度主要在记录边的信息
无向图的存储中,只需要将一条无向边拆成两条即可
邻接矩阵:用一个二维数组 edg[N][N] 表示
edg[i][j] 就对应由 i 到 j 的边信息
edg[i][j] 可以记录 Bool,也可以记录边权
缺点:如果有重边有时候不好处理
空间复杂度 O(V2)
点度(出边入边条数)等额外信息也是很好维护的
模板(传说中的链式前向星/链式存图):
#include<bits/stdc++.h>
using namespace std;
const int N=;
int n,m,s;
struct Ed{
int next,dis,to;
}ed[N];
int ed_num;
int tail[N];
inline void add(int from,int to,int dis){
ed_num++;
ed[ed_num].dis=dis;
ed[ed_num].to=to;
ed[ed_num].next=tail[from];
tail[from]=ed_num;
}
int main(){
scanf("%d%d%d",&n,&m,&s);
for(int i=;i<=m;i++){
int a,b,c;
add(a,b,c);
}
//use it
for(int i=tail[s];i;i=ed[i].next){
//bla bla bla...
}
printf("What ever it takes\n");
return ;
}
2.vector
用于灵活储存一定范围内(指不会爆栈)数据的变长数组
队列好像就是这么存的,然而这个东西很慢...
最小生成树:
3.kruskal(前置知识:并查集):
将每条边排序,按从小到大加入生成树,并将两端点合并作同一并查集,
如果边的两端点再加入此边之前已属于同一集合,说明其已经连通,无需加入这一条边
如果当前边数已经为n-1,则跳出循环,已经找到目标
#include<bits/stdc++.h>
using namespace std;
int n,m;
int fa[];
inline int father(int t){
if(fa[t]!=t) fa[t]=father(fa[t]);
return fa[t];
}
inline void u(int l,int r){
int fl=father(l);
int fr=father(r);
if(fl!=fr) fa[fl]=fr;
}
struct ed{
int len;
int begin,end;
}dis[];
inline bool cmp(ed a,ed b){
return a.len<b.len;
}
int sum;
int num;
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
fa[i]=i;
for(int i=;i<=m;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
dis[i].begin=x;
dis[i].end=y;
dis[i].len=z;
}sort(dis+,dis++m,cmp);
for(int i=;i<=m;i++){
if(father(dis[i].begin)!=father(dis[i].end)){
u(dis[i].begin,dis[i].end);
sum+=dis[i].len;
num++;
}
if(num==n-){
cout<<sum<<endl;
return ;
}
}
cout<<"orz";
return ;
}
4.kosaraju(偷一波baidu)
对原图G进行深度优先遍历,记录每个节点的离开时间num[i]
选择具有最晚离开时间的顶点,对反图GT(由G的反向边组成)进行遍历,删除能够遍历到的顶点
这些顶点构成一个强连通分量(好像是互相连通的意思)
如果还有顶点没有删除,继续步骤2,否则算法结束
5.prim
思想:一开始有n个连通块,从起点开始,每次找距离最短的连通块连到一起
代码:咕咕咕
总的来说,最小生成树还是用kruskal吧...
最短路问题(共四个,真正意义上是3个):
给一个有向图,求s到e的最短距离(距离:两点之间边的边权和)
6.松弛操作——最短路算法的本质
dis[i][j]<=dis[i][k]+dis[k][j]
7.floyd(之前打的一直是错的):
三层循环找上面式子的i(起点),j(终点),k(中间点)
代码(邻接矩阵):
#include <bits/stdc++.h>
using namespace std;
const int N=;
const int inf=<<;
int d[N][N],n,m;
int main(){
cin>>n>>m;
for(int i=;i<=n;i++)
for(int j=;j<=n;j++) d[i][j]=inf;
for(int u,v,w,i=;i<=m;i++)
cin>>u>>v>>w,d[u][v]=min(d[u][v],w);
for(int k=;k<=n;k++)
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
printf("并没有输出\n");
return ;
}
要点:
1.先要枚举k,不然就是错的
2.把最大边初始化为inf(infinite),即为极大(无穷),不然你min怎么取...(全是0)
3.主要思路是在k=t循环结束时,dis[i][j]只经过1,2...t,此时dis[i][j]为真实值
4.负权环(一个可以跑死SPFA的东西):
Floyd跑完以后判断下有没有负边权就好,Floyd能处理,但SPFA会凉,
具体解决办法就是加一个计数变量,如果处理次数大于预期最大次数,就输出无解或关闭程序
单源最短路问题:
8.Bellman-Ford:
枚举每一条边(e(u,v,w))并松弛d(v)=min{d(v),d(u)+w}
松弛n次即可,可以加一个优化,
添加bool变量判断有没有进行松弛,没有就不用再进行该层循环了
9.SPFA:
把需要松弛的边用queue存储,等松弛时拿出操作,代码:
#include<bits/stdc++.h>
using namespace std;
const int inf=;
bool vis[];
int dis[];
int tail[];
struct Ed{
int next,to,dis;
}ed[];
int m,n,s;
int num_edge;
inline void join(int from,int to,int dis){
num_edge++;
ed[num_edge].dis=dis;
ed[num_edge].to=to;
ed[num_edge].next=tail[from];
tail[from]=num_edge;
}
queue<int> q;
int main(){
scanf("%d%d%d",&n,&m,&s);
for(int i=;i<=m;i++)
dis[i]=inf;
vis[s]=;
dis[s]=;
for(int i=;i<=m;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
join(x,y,z);
}
q.push(s);
while(!q.empty()){
int now=q.front();
q.pop();
vis[now]=;
for(int i=tail[now];i;i=ed[i].next){
int end=ed[i].to;
if(dis[end]>dis[now]+ed[i].dis){
dis[end]=dis[now]+ed[i].dis;
if(!vis[end]){
q.push(end);
vis[end]=;
}
}
}
}
for(int i=;i<=n;i++){
printf("%d ",dis[i]);
}
return ;
}
优化(上面提到过):
记录每个点加入queue次数,如果大于n-1次,则这个点可能已经在负权环里面无限快乐了
SPFA跑稀疏图很快,跑网格图就非常慢了
10.dijkstra
前向星版本完成辣!
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int inf=;
struct Ed{
int to,dis,next;
}ed[];
int tail[];
int ed_num;
inline void add(int from,int to,int dis){
ed_num++;
ed[ed_num].dis=dis;
ed[ed_num].to=to;
ed[ed_num].next=tail[from];
tail[from]=ed_num;
}
int dis[];
bool vis[];
int n,m,s;
int main(){
scanf("%d%d%d",&n,&m,&s);
for(int i=;i<=n;i++)
dis[i]=inf;
dis[s]=;
vis[s]=;
for(int i=;i<=m;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
for(int i=tail[s];i;i=ed[i].next)
dis[ed[i].to]=ed[i].dis;
for(int i=;i<=n;i++){
int now=inf;
int k=;
for(int j=;j<=n;j++)
if((!vis[j])&&(now>dis[j])){
now=dis[j];
k=j;
}
if(k==) break;
vis[k]=;
for(int j=tail[k];j;j=ed[j].next)
if(dis[ed[j].to]>dis[k]+ed[j].dis)
dis[ed[j].to]=dis[k]+ed[j].dis;
}
for(int i=;i<=n;i++) printf("%d ",dis[i]);
return ;
}
我开心地交了上去٩(๑>◡<๑)۶
.......
11.DAG有向无环图与拓扑排序
即找一个按顺序遍历所有节点的顺序,显然答案不唯一
思想类似于完成一些任务,而某些任务有其前置任务
思路:
先将所有入度为0的点入队,再依次广搜,把入队的点删除,将它到达的点入度-1
依次重复步骤,直到队列为空
老师代码(懒得自己敲了):
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + ;
const int inf = << ;
struct edge{
int u, v;
};
vector<edge> edg[N];
int n, m, outdeg[N], ans[N];
queue<int> Queue;
void add(int u, int v)
{
edg[u].push_back((edge){u, v});
}
int main()
{
cin >> n >> m;
for (int u, v, i = ; i <= m; i++)
cin >> u >> v, add(v, u), outdeg[u]++; for (int i = ; i <= n; i++)
#include <bits/stdc++.h> using namespace std; const int maxn = ;
struct edge {
int u, v, w;
}edg[maxn];
int n, m, p[maxn], Q, dep[maxn];
vector<edge> adj[maxn]; // edges in MST
bool cmp(edge a, edge b)
{return a.w < b.w;}
int findp(int t)
{return p[t] ? p[t] = findp(p[t]) : t;} bool merge(int u, int v)
{
u = findp(u); v = findp(v);
if (u == v) return false;
p[u] = v; return true;
} int anc[maxn][], maxw[maxn][];
void dfs(int u)
{
for (int j = ; j < ; j++)
anc[u][j] = anc[anc[u][j - ]][j - ],
maxw[u][j] = max(maxw[u][j - ], maxw[anc[u][j - ]][j - ]);
for (unsigned i = ; i < adj[u].size(); ++i)
{
int v = adj[u][i].v, w = adj[u][i].w;
if (v != anc[u][])
dep[v] = dep[u] + , anc[v][] = u, maxw[v][] = w, dfs(v);
}
}
int solve(int u, int v)
{
int res = ;
if (dep[u] < dep[v]) swap(u, v);
for (int d = dep[u] - dep[v], j = ; d ; d >>= , ++j)
if (d & ) res = max(res, maxw[u][j]), u = anc[u][j];
//adjust u & v to the same depth
if (u == v) return res; //u & v meet now for (int j = ; j >= ; j--)
if (anc[u][j] != anc[v][j]) // if anc[u][j] & anc[v][j] dont meet together, then jump u & v
res = max(res, maxw[u][j]),
res = max(res, maxw[v][j]),
u = anc[u][j], v = anc[v][j];
//now u & v 's lca must be their parent now, in an easy word, it's anc[u][0] or anc[v][0] res = max(res, maxw[u][]);
res = max(res, maxw[v][]);
u = anc[u][]; v = anc[v][];
return res;
}
int main()
{
cin >> n >> m >> Q;
for (int i = , u, v, w; i <= m; i++)
cin >> u >> v >> w, edg[i] = (edge){u, v, w};
sort(edg + , edg + m + , cmp);
for (int i = , u, v; i <= m; i++)
if (merge(u = edg[i].u, v = edg[i].v))
adj[u].push_back(edg[i]),
adj[v].push_back((edge){v, u, edg[i].w}); dfs();
for (int u, v, i = ; i <= Q; i++)
cin >> u >> v, cout << solve(u, v) << endl;
}
if (outdeg[i] == ) Queue.push(i);
for (int i = ; i <= n; i++)
{
if (Queue.empty())
{printf("Not DAG"); return ;}
int u = Queue.front(); Queue.pop(); ans[n - i + ] = u;
for (int e = ; e < edg[u].size(); e++)
{
int v = edg[u][e].v;
if (--outdeg[v] == ) Queue.push(v);
}
}
}
11.lca(最近公共祖先)
思路,先将二者跳到同一深度,再将x和y以尽量远的高度向上跳,直到父节点唯一
代码:
#include <bits/stdc++.h> using namespace std; const int maxn = ;
struct edge {
int u, v, w;
}edg[maxn];
int n, m, p[maxn], Q, dep[maxn];
vector<edge> adj[maxn]; // edges in MST
bool cmp(edge a, edge b)
{return a.w < b.w;}
int findp(int t)
{return p[t] ? p[t] = findp(p[t]) : t;} bool merge(int u, int v)
{
u = findp(u); v = findp(v);
if (u == v) return false;
p[u] = v; return true;
} int anc[maxn][], maxw[maxn][];
void dfs(int u)
{
for (int j = ; j < ; j++)
anc[u][j] = anc[anc[u][j - ]][j - ],
maxw[u][j] = max(maxw[u][j - ], maxw[anc[u][j - ]][j - ]);
for (unsigned i = ; i < adj[u].size(); ++i)
{
int v = adj[u][i].v, w = adj[u][i].w;
if (v != anc[u][])
dep[v] = dep[u] + , anc[v][] = u, maxw[v][] = w, dfs(v);
}
}
int solve(int u, int v)
{
int res = ;
if (dep[u] < dep[v]) swap(u, v);
for (int d = dep[u] - dep[v], j = ; d ; d >>= , ++j)
if (d & ) res = max(res, maxw[u][j]), u = anc[u][j];
//adjust u & v to the same depth
if (u == v) return res; //u & v meet now for (int j = ; j >= ; j--)
if (anc[u][j] != anc[v][j]) // if anc[u][j] & anc[v][j] dont meet together, then jump u & v
res = max(res, maxw[u][j]),
res = max(res, maxw[v][j]),
u = anc[u][j], v = anc[v][j];
//now u & v 's lca must be their parent now, in an easy word, it's anc[u][0] or anc[v][0] res = max(res, maxw[u][]);
res = max(res, maxw[v][]);
u = anc[u][]; v = anc[v][];
return res;
}
int main()
{
cin >> n >> m >> Q;
for (int i = , u, v, w; i <= m; i++)
cin >> u >> v >> w, edg[i] = (edge){u, v, w};
sort(edg + , edg + m + , cmp);
for (int i = , u, v; i <= m; i++)
if (merge(u = edg[i].u, v = edg[i].v))
adj[u].push_back(edg[i]),
adj[v].push_back((edge){v, u, edg[i].w}); dfs();
for (int u, v, i = ; i <= Q; i++)
cin >> u >> v, cout << solve(u, v) << endl;
}
清北学堂(2019 5 2) part 5的更多相关文章
- 清北学堂2019.8.10 & 清北学堂2019.8.11 & 清北学堂2019.8.12
Day 5 杨思祺(YOUSIKI) 今天的难度逐渐上升,我也没做什么笔记 开始口胡正解 今天的主要内容是最小生成树,树上倍增和树链剖分 最小生成树 Prim 将所有点分为两个集合,已经和点 1 连通 ...
- 清北学堂2019.7.18 & 清北学堂2019.7.19
Day 6 钟皓曦 经典题目:石子合并 可以合并任意两堆,代价为数量的异或(^)和 f[s]把s的二进制所对应石子合并成一堆所花代价 枚举s的子集 #include<iostream> u ...
- 清北学堂2017NOIP冬令营入学测试P4745 B’s problem(b)
清北学堂2017NOIP冬令营入学测试 P4745 B's problem(b) 时间: 1000ms / 空间: 655360KiB / Java类名: Main 背景 冬令营入学测试 描述 题目描 ...
- 清北学堂2017NOIP冬令营入学测试 P4744 A’s problem(a)
清北学堂2017NOIP冬令营入学测试 P4744 A's problem(a) 时间: 1000ms / 空间: 655360KiB / Java类名: Main 背景 冬令营入学测试题,每三天结算 ...
- 济南清北学堂游记 Day 1.
快住手!这根本不是暴力! 刷了一整天的题就是了..上午三道题的画风还算挺正常,估计是第一天,给点水题做做算了.. rqy大佬AK了上午的比赛! 当时我t2暴力写挂,还以为需要用啥奇怪的算法,后来发现, ...
- 清明培训 清北学堂 DAY1
今天是李昊老师的讲授~~ 总结了一下今天的内容: 1.高精度算法 (1) 高精度加法 思路:模拟竖式运算 注意:进位 优化:压位 程序代码: #include<iostream>#in ...
- 7月清北学堂培训 Day 3
今天是丁明朔老师的讲授~ 数据结构 绪论 下面是天天见的: 栈,队列: 堆: 并查集: 树状数组: 线段树: 平衡树: 下面是不常见的: 主席树: 树链剖分: 树套树: 下面是清北学堂课程表里的: S ...
- <知识整理>2019清北学堂提高储备D2
简单数据结构: 一.二叉搜索树 1.前置技能: n/1+n/2+……+n/n=O(n log n) (本天复杂度常涉及) 2.入门题引入: N<=100000. 这里多了一个删除的操作,因此要 ...
- <知识整理>2019清北学堂提高储备D3
全天动态规划入门到入坑... 一.总概: 动态规划是指解最优化问题的一类算法,考察方式灵活,也常是NOIP难题级别.先明确动态规划里的一些概念: 状态:可看做用动态规划求解问题时操作的对象. 边界条件 ...
- 清北学堂(2019 4 28 ) part 1
今天主要用来铺路,打基础 枚举 没什么具体算法讲究,但要考虑更优的暴力枚举方法,例如回文质数,有以下几种思路: 1.挨个枚举自然数,再一起判断是否是回文数和质数,然而一看就不是最优 2.先枚举质数再判 ...
随机推荐
- ASP.NET Core快速入门(第2章:配置管理)- 学习笔记(转载)
原文地址:https://mp.weixin.qq.com/s?__biz=MjM5NjMzMzE2MA==&mid=2451733443&idx=2&sn=6d01721c5 ...
- (二十四)golang--错误处理
在默认情况下,遇到错误时,程序会崩溃: 在发生错误时,我们可以捕获错误,使程序可以继续运行,并抛出错误提示: 错误处理: (1)Go语言追求简洁优雅,所以不支持传统的try catch finally ...
- Element-ui 2.8.0版本中提升表格性能,做了哪些事情,原理是什么
背景 项目中一直用element-ui,之前用el-table的时候,发现表格数据较多时,滑动表格就会很卡.我们的表格中只有200行数据,每行大概有30的字段,表格滑动就卡的不行.在Element-u ...
- 联邦学习PySyft
Steps involved in the Federated Learning Approach The mobile devices download the global ML model Da ...
- RestTemplate的三种请求方式
转载 https://blog.csdn.net/qq_36364521/article/details/84203133
- NimSystem实现
题目 题目比较长,我直接放截图吧 简述 一个比较经典的类与对象的题目,三个类实现了一个比较简单的系统,具体的每个类的要求可以从上面的题目描述中看出(只要你有耐心读完..),不再赘述,代码如下 代码实现 ...
- WinForm 程序在系统托盘显示
前言 有些程序在关闭或最小化的时候会隐藏在系统托盘中,双击或右击会重新显示,winform实现其功能很简单,这边就简单的介绍下. 实现 在winform实现托盘使用notifyIcon控件,如果加菜单 ...
- Django的路由系统:URL
一. URLconf配置 基本格式 from django.conf.urls import url urlpatterns = [ url(正则表达式, views视图,参数,别名), ] 参数说明 ...
- css中的行内元素和块级元素总结
块级元素 <address>, <button>, <caption>, <dd>, <del>, <div>, & ...
- python高级语法1
1.解压序列赋值给多个变量 p = (4, 5, 6) x, y , z = p # x = 4, y = 5, z = 6 若可迭代对象超过变量个数,会抛出ValueError 用处:可以解压赋值任 ...