图论板子
图论板子
1.最短路
(1)Dijkstra
无堆优化:
int n,m,G[N][N],d[N];
bool vis[N] = {false};
void Dij(int s){
for(int i = 1;i <= n;i++) d[i] = inf;
d[s] = 0;
for(int i = 1;i <= n;i++){
int x,_m = inf;
for(int j = 1;j <= n;j++){//找到离起点距离最短并且没有找过的点
if(!vis[j] && d[j] <= _m) _m = d[x=j];
}
vis[x] = true;//找到了最短路,并且更新一遍最短路树
for(int j = 1;j <= n;j++)d[j] = min(d[j],d[x]+G[x][j]);
}
}
堆优化:
ll n,m,s;
struct edge{
ll to,w;
edge(ll v,ll ww){to = v;w = ww;}
};
struct node{
ll id,dis;
node(ll x,ll y){id = x;dis = y;}
bool operator < (const node &a)const
{return dis > a.dis;}
};
vector<edge> e[N];
void add(ll u,ll v,ll w){
e[u].push_back({v,w});
}
ll d[N];
bool done[N];
void dijkstra(){
for(ll i = 1;i <= n;i++){
d[i] = (i==s)?0:inf;
done[i] = false;
}
priority_queue<node> q;
q.push(node(s,d[s]));
while(!q.empty()){
node x = q.top();
q.pop();
if(done[x.id]) continue;
done[x.id] = true;
for(ll i = 0;i < e[x.id].size();i++){
edge u = e[x.id][i];
if(done[u.to])continue;
if(d[u.to] > x.dis+u.w){
d[u.to] = x.dis+u.w;
q.push(node(u.to,d[u.to]));
}
}
}
}
(2)bellman-ford
int n,m,s,d;
struct Edge{
int u,v,w;
Edge(int _u = 0,int _v = 0,int _w = 0):u(_u),v (_v),w(_w){}
};
vector<Edge> edge;
int dis[N];
void add(int u,int v,int w){
edge.push_back(Edge(u,v,w));
}
bool bellman(){
for(int i = 1;i <= n;i++) dis[i] = inf;
dis[s] = 0;
for(int i = 1;i <= n-1;i++){
bool flag = false;
for(int j = 0;j < edge.size();j++){
int u = edge[j].u,v= edge[j].v,w = edge[j].w;
if(dis[v] > dis[u]+w){
dis[v] = dis[u]+w;
flag = true;
}
}
if(!flag) return true;
}
for(int i = 0;i < edge.size();i++)
if(dis[edge[i].v] > dis[edge[i].u])
return false;
return true;
}
(3)Spfa
int n,m,s,t;//点数,边数,起点,终点
struct Edge{
int u,v;
int w;
Edge(int _u = 0,int _v = 0,int _w = 0):u(_u),v(_v),w(_w){}
};
vector<Edge> G[N];
void add(int u,int v,int w){
G[u].push_back(Edge(u,v,w));
}
bool vis[N];//检测是否在队列中
int cnt[N],dis[N];//cnt表示进入队列次数,dis表示距离
queue<int>q;
bool spfa(){
memset(vis,false,sizeof(vis));
for(int i = 1;i <= n;i++)dis[i] = inf;
vis[s] = true;dis[s] = 0;
while(!q.empty()) q.pop();
q.push(s);
memset(cnt,0,sizeof cnt);
cnt[s] = 1;
while(!q.empty()){
int u = q.front();
q.pop();vis[u] = false;
for(auto e:G[u]){
int v = e.v;
if(dis[v] > dis[u]+e.w){
dis[v] = dis[u]+e.w;
if(!vis[v]){
vis[v] = true;
q.push(v);
if(++cnt[v] > n)return false;
//因为存在一个点被访问了超过n次所以该回路存在负环回路
}
}
}
}
return true;
}
2.最小生成树
(1) Kruskal算法(和并查集的板子一起了)
ll n,m,cnt = 0;
struct Edge{
ll u,v,w;
bool operator < (const Edge &x)const{return x.w > w;}
}edge[N];
ll f[N] = {0};
ll find(ll x){
return x == f[x]?x:f[x] = find(f[x]);
}
void _union(ll x,ll y){
x = find(x),y = find(y);
if(x != y) f[x] = y;
}
ll ans = 0;
void Kruskal(){
for(int i = 1;i <= n;i++) f[i] = i;
sort(edge+1,edge+m+1);
ll ans = 0;
for(int i = 1;i <= m;i++){
ll u = edge[i].u,v = edge[i].v;
if(find(u) != find(v)){
ans += edge[i].w;
_union(u,v);
}
}
}
(2)prim算法
//dij改一下就是了
bool vis[N];
ll lowc[N],n;
void prim(){
memset(vis,false,sizeof vis);
ll ans = 0;
vis[1] = true;
for(ll i = 2;i <= n;i++) lowc[i] = G[1][i];
for(ll i = 2;i <= n;i++){
ll minc = inf;
ll tmp = -1;
for(ll j = 1;j <= n;j++){
if(!vis[j] && minc > lowc[j]){
minc = lowc[j];
tmp = j;
}
}
ans += minc;
vis[tmp] = true;
for(ll j = 1;j <= n;j++){
if(!vis[j] && lowc[j] > G[tmp][j]){
lowc[j] = G[tmp][j];
}
}
}
cout<<ans<<endl;
}
3.联通性
tarjan
int n,m;
struct Edge{
ll to,nxt;
}edge[N];
int head[N],tot;
int low[N],dfn[N],sta[N],co[N];
bool is_sta[N] = {false};
int cnt = 0,top = 0;
int scc = 0;//连通分量的个数
int num[N];//每个强连通分量的点数
void add(int u,int v){
edge[tot].to = v;
edge[tot].nxt = head[u];
head[u] = tot++;
}
void tarjan(int u){
int v;
low[u] = dfn[u] = ++cnt;
sta[top++] = u;
is_sta[u] = true;
for(int i = head[u];~i;i = edge[i].nxt){
v = edge[i].to;
if(!dfn[v]){
tarjan(v);
low[u] = min(low[u],low[v]);
}
else if(is_sta[v]) low[u] = min(low[u],dfn[v]);
}
if(low[u] == dfn[u]){
scc++;
do{
v = sta[--top];
is_sta[v] = false;
co[v] = scc;
num[scc]++;
}while(v != u);
}
}
void solve(){
memset(dfn,0,sizeof(dfn));
memset(is_sta,false,sizeof(is_sta));
memset(num,0,sizeof(num));
cnt = top = scc = 0;
for(int i = 1;i <= n;i++){
if (!dfn[i])tarjan(i);
}
}
void init(){
tot = 0;
memset(head,-1,sizeof(head));
}
(2)Kosaraju
struct Edge{
int to,next;
}edge1[N],edge2[N];
int head1[N],head2[N];
bool ok1[N],ok2[N];
int tot1,tot2;
int cnt1,cnt2;
int st[N],be[N],scc[N];//对原图dfs,标记时间;记录每一个点在哪个连通分量;记录某一连通分量的点数
int tmp = 0;//中间变量
void add(int u,int v){
edge1[tot1].to = v;edge1[tot1].next = head1[u];head1[u] = tot1++;//建立正图
edge2[tot2].to = u;edge2[tot2].next = head2[v];head2[v] = tot2++;//建立反图
}
void dfs1(int u){
ok1[u] = true;
for(int i = head1[u];~i;i = edge1[i].next){
int v = edge1[i].to;
if(!ok1[v]) dfs1(v);
}
st[cnt1++] = u;
}
void dfs2(int u){
ok2[u] = true;
tmp++;
be[u] = cnt2;
for(int i = head2[u];~i;i = edge2[i].next){
int v = edge2[i].to;
if(!ok2[v]) dfs2(v);
}
}
void slove(){
memset(ok1,false,sizeof ok1);
memset(ok2,false,sizeof ok2);
cnt1 = cnt2 = 0;
for(int i = 1;i <= n;i++){
if(!ok1[i]) dfs1(i);
}
for(int i = cnt1-1;i >= 0;i--){
if(!ok2[st[i]]){
tmp = 0;
dfs2(st[i]);
scc[cnt2++] = tmp;
}
}
}
4.二分图
(1)匈牙利算法
int m,n; //左右的集合的元素数量
int G[N][N]; //邻接矩阵存图
int p[N] = {0}; //右侧元素对应的左侧元素
bool vis[N]; //记录右侧元素是否被访问过
bool match(int i){
for(int j = 1;j <= n;j++){
if(G[i][j] && !vis[j]){ //有边且未访问
vis[j] = true; //记录状态被访问过
if(p[j] == 0 || match(p[j])){ //暂无匹配,原匹配的左侧元素可以找到新元素
p[j] = i; //当前左侧元素成为当前右侧元素的新匹配
return true; //返回匹配成功
}
}
}
return false; //循环结束,未找到匹配,返回匹配失败
}
int Hungarian(){
int cnt = 0;
for(int i = 1;i <= m;i++){
memset(vis,0,sizeof vis); //重置vis数组
if(match(i)) cnt++;
}
return cnt;
}
网络流Dinic
struct Edge{ll to,w,nxt;}edge[M];
ll head[N],cnt = 1;
void add(ll u,ll v,ll w){
edge[++cnt] = {v,w,head[u]};
head[u] = cnt;
}
void add2(ll u,ll v,ll w){
add(u,v,w);
add(v,u,0);
}
ll s,t,lv[N],cur[N];
bool bfs(){
memset(lv,-1,sizeof lv);
lv[s] = 0;
memcpy(cur, head, sizeof(head));
queue<ll>q;q.push(s);
while(!q.empty()){
ll p = q.front();q.pop();
for(ll eg = head[p];eg;eg = edge[eg].nxt){
ll to = edge[eg].to,vol = edge[eg].w;
if(vol > 0 && lv[to] == -1) lv[to] = lv[p] +1,q.push(to);
}
}
return lv[t] != -1;
}
ll dfs(ll p = s,ll flow = inf){
if(p == t)return flow;
ll rmn = flow;
for(ll &eg = cur[p];eg;eg = edge[eg].nxt){
if(!rmn)break;
ll to = edge[eg].to,vol = edge[eg].w;
if(vol > 0 && lv[to] == lv[p]+1){
ll c = dfs(to,min(vol,rmn));
rmn -= c;
edge[eg].w -= c;
edge[eg^1].w += c;
if(rmn == 0)break;
}
}
return flow-rmn;
}
ll dinic(){
ll ans = 0;
while(bfs()) ans += dfs();
return ans;
}
费用流
#include <iostream>
#include <queue>
#define ll long long
const ll N = 5e3+50,M = 5e4+50;
const ll inf = 0x3f3f3f3f;
using namespace std;
ll head[N],cnt = 1;
//将EK的bfs变为spfa
struct Edge{
ll to,w,cost,nxt;
}edge[M*2];
void add(ll u,ll v,ll w,ll c){
edge[++cnt] = {v,w,c,head[u]};
head[u] = cnt;
}
void add2(ll u,ll v,ll w,ll cost){
add(u,v,w,cost);
add(v,u,0,-cost);
}
ll s,t,dis[N],cur[N];
bool inq[N],vis[N];
queue<ll>Q;
bool spfa(){
while(!Q.empty()) Q.pop();
copy(head,head+N,cur);
fill(dis,dis+N,inf);
dis[s] = 0;
Q.push(s);
while(!Q.empty()){
ll p = Q.front();
Q.pop();
inq[p] = false;
for(ll e = head[p];e;e = edge[e].nxt){
ll to = edge[e].to,vol = edge[e].w;
if(vol > 0 && dis[to]>dis[p]+edge[e].cost){
dis[to] = dis[p] + edge[e].cost;
if(!inq[to]){
Q.push(to);
inq[to] = true;
}
}
}
}
return dis[t] != inf;
}
ll dfs(ll p = s,ll flow = inf){
if(p == t) return flow;
vis[p] = true;
ll rmn = flow;
for(ll eg = cur[p];eg && rmn;eg = edge[eg].nxt){
cur[p] = eg;
ll to = edge[eg].to,vol = edge[eg].w;
if(vol > 0 && !vis[to]&&dis[to] == dis[p]+edge[eg].cost){
ll c = dfs(to,min(vol,rmn));
rmn -= c;
edge[eg].w -= c;
edge[eg^1].w += c;
if(rmn == 0)break;`
}
}
vis[p] = false;
return flow-rmn;
}
ll maxflow = 0,mincost = 0;
void dinic(){
while(spfa()){
ll flow = dfs();
maxflow += flow;
mincost += dis[t]*flow;
}
}
int main() {
ios::sync_with_stdio(false);
ll n,m;
cin>>n>>m>>s>>t;
while(m--){
ll u,v,w,c;cin>>u>>v>>w>>c;
add2(u,v,w,c);
}
dinic();
cout<<maxflow<<' '<<mincost<<endl;
return 0;
}
LCA
(1)树上倍增
struct Edge{
ll to,nxt;
}edge[2*N];
ll head[2*N],tot;
void init(){
for(int i=0;i<2*N;++i){
edge[i].nxt = -1;
head[i] = -1;
}
tot = 0;
}
void add(ll u,ll v){
edge[tot].to = v;
edge[tot].nxt = head[u];
head[u] = tot++;
}
ll fa[N][20],deep[N];
//fa[x][i]表示,x的第2^i个祖先,fa[x][i] = fa[fa[x][i-1]][i-1]
//deep[x]是x的深度
void dfs(ll u,ll father){//求u的深度
deep[u] = deep[father]+1;
fa[u][0] = father;
for(ll i = 1;(1<<i)<=deep[u];i++){
fa[u][i] = fa[fa[u][i-1]][i-1];
}
for(ll i = head[u];~i;i = edge[i].nxt){
if(edge[i].to != father){
dfs(edge[i].to,u);
}
}
}
ll LCA(ll x,ll y){
if(deep[x] < deep[y]) swap(x,y);
for(ll i = 19;i >= 0;i--){//先让x跳到y的层数
if(deep[x]-(1<<i) >= deep[y]) x = fa[x][i];
}
if(x == y) return x;
for(ll i = 19;i >= 0;i--){//让x,y一起跳,最多只能跳到LCA的下一层
if(fa[x][i] != fa[y][i]){
x = fa[x][i];
y = fa[y][i];
}
}
return fa[x][0];
}