引文
邻接矩阵
邻接表
bool vis[N];
vector<int> g[N];//邻接表存图
int maxr[N];
void dfs(int u,int d) {
if(maxr[u]) return;
maxr[u]=d;
for(int i=0;i<g[u].size();i++)
dfs(g[u][i],d);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin>>n>>m;
rep(i, 1, m) {
int a, b;
cin >> a >> b ;
g[b].pb(a);//反向建边
}
per(i,n,1) dfs(i,i);
rep(i,1,n) cout<<maxr[i]<<' ';
return 0;
}
边集数组
链式向前星(网络流)
优点
功能包含邻接表和链式邻接表
struct edge {
int v, w, ne;
};
edge e[N];
int h[N], idx;
bool vis[N];
void add(int a, int b, int c) {
e[idx] = {b, c, h[a]};
h[a] = idx++;
}
void dfs(int u) {
vis[u] = true; // 标记当前节点已访问
for(int i = h[u]; ~i; i = e[i].ne) {
int v = e[i].v, w = e[i].w;
if(vis[v]) continue; // 如果节点v已经访问过,跳过它
printf("%d %d %d\n", u, v, w);
dfs(v); // 递归访问节点v
}
}
int main() {
int n, m;
cin >> n >> m;
ms(h, -1);
ms(vis, false);
rep(i, 1, m) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
add(b, a, c);
}
dfs(1); // 从节点1开始进行DFS遍历
return 0;
}
朴素DIjkstra
int n,m,s,t;
double d[N],e[N][N];//邻接矩阵
int vis[N];
void dijk(){
ms(vis,0);
d[s]=1;
rep(i,1,n){
int u=-1;
rep(j,1,n)
if(!vis[j] && (u==-1 || d[j]>d[u])) u=j;
vis[u]=1;
rep(j,1,n) d[j]=max(d[j],d[u]*e[u][j]);
}
}
int main(){
cin>>n>>m;
while(m--){
int a,b,c;
cin>>a>>b>>c;
double t=(100.0-c)/100;//! 100.0
e[a][b]=e[b][a]=max(e[a][b],t);
}
cin>>s>>t;
dijk();
printf("%.8lf",100/d[t]);
return 0;
}
堆优化Dijktra
使用
priority_queue
struct edge{
int v,w;
};
int n,m,s;
vector<edge> e[N];//邻接表存图
int d[N],vis[N];
void dijkstra(int s){
ms(d,0x3f);
d[s]=0;
//小根堆
//priority_queue <PII,vector <PII>,greater <PII>> q;
priority_queue<PII> q;
q.push({0,s});
while(q.size()){
int u=q.top().se;
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(auto ed:e[u]){
int v=ed.v,w=ed.w;
if(d[v]>d[u]+w[i]){
d[v]=d[u]+w[i];
q.push({-d[v],v});
}
}
}
}
int main(){
cin>>n>>m>>s;
while(m--){
int u,v,w;
cin>>u>>v>>w;
e[u].pb({v,w});
}
dijkstra(s);
rep(i,1,n) cout<<d[i]<<' ';
return 0;
}
SPFA
bellman_Ford的优化
vector
int n,m,T;
struct edge{
int v,w;
};
vector<edge> e[N];
int vis[N],d[N],cnt[N];
bool spfa(int s){
ms(d,0x3f);
ms(vis,0);
ms(cnt,0);
d[s]=0;
vis[s]=1;
queue<int> q;
q.push(s);
while(q.size()){
int u=q.front();
q.pop(); vis[u]=0;
for(auto ed : e[u]){
int v=ed.v,w=ed.w;
if(d[v]>d[u]+w){
d[v]=d[u]+w;
cnt[v]=cnt[u]+1;
if(cnt[v]>=n) return 1;
if(!vis[v]){
q.push(v);
vis[v]=1;
}
}
}
}
return 0;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>T;
while(T--){
ms(e,0);//清空e的所有元素
cin>>n>>m;
rep(i,1,m){
int a,b,c;
cin>>a>>b>>c;
e[a].pb({b,c});
if(c>=0) e[b].pb({a,c});
}
printf(spfa(1)?"YES":"NO");
puts("");
}
return 0;
}
链式前向星–>静态数组
int n,m,T;
int e[N],ne[N],h[N],w[N],idx;
int vis[N],d[N],cnt[N];
void add(int a,int b,int c){
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
bool spfa(int s){
ms(d,0x3f);
ms(vis,0);
ms(cnt,0);
d[s]=0;
queue<int> q;
vis[s]=1;//打标记
q.push(s);
while(q.size()){
int u=q.front();
q.pop(); vis[u]=0;//去标记
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(d[v]>d[u]+w[i]){
d[v]=d[u]+w[i];
cnt[v]=cnt[u]+1;
if(cnt[v]>=n) return 1;
if(!vis[v]){
q.push(v);
vis[v]=1;
}
}
}
}
return 0;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>T;
while(T--){
ms(h,-1);
idx=0;
cin>>n>>m;
rep(i,1,m){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
if(c>=0) add(b,a,c);
}
printf(spfa(1)?"YES":"NO");
puts("");
}
return 0;
}
Floyd
int n,m,ans=1e8;//最多为1e8,不能为1e9
int d[N][N],w[N][N];
void floyd(){
rep(k,1,n){
for(int i=1;i<k;i++)
for(int j=i+1;j<k;j++)
ans=min(ans,d[i][j]+w[j][k]+w[k][i]);
rep(i,1,n)
rep(j,1,n)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
}
int main(){
cin>>n>>m;
rep(i,1,n)
rep(j,1,n)
if(i!=j) w[i][j]=1e8;
while(m--){
int a,b,c;
cin>>a>>b>>c;
w[a][b]=w[b][a]=c;
}
memcpy(d,w,sizeof d);
floyd();
if(ans==1e8) cout<<"No solution.";
else cout<<ans;
return 0;
}
Prim算法求最小生成树
H e a p − P r i m \color{RED}{Heap-Prim} Heap−Prim
int n,m,ans,cnt;
struct edge{
int v,w;
};
vector<edge> e[N];
int d[N],vis[N];
bool prim(int s){
ms(d,0x3f);
d[s]=0;
priority_queue<PII> q;
q.push({0,s});
while(q.size()){
int u=q.top().se;
q.pop();
if(vis[u]) continue;
vis[u]=1;
ans+=d[u],cnt++;
for(auto ed : e[u]){
int v=ed.v,w=ed.w;
if(d[v]>w){
d[v]=w;
q.push({-d[v],v});
}
}
}
return cnt==n;
}
int main(){
cin>>n>>m;
while(m--){
int a,b,c;
cin>>a>>b>>c;
e[a].pb({b,c});
e[b].pb({a,c});
}
if(!prim(1)) cout<<"orz";
else cout<<ans;
return 0;
}
Kruskal算法求最小生成树
- 利用
并查集
求MST- 看到这个n-1发现两个算法的一个区别是
找短边
,一个找距离最近的点
.- 简要思路:判断是否在同一个集合,如果不在,则加入到mst,且合并两个集合
LCA
倍增算法
1 < < 20 ≈ 1 e 6 \color{BLUE}{1<<20 ≈1e6} 1<<20≈1e6
1、
dep[u]
:存u点的深度
2、fa[u][i]
:存从u点向上跳 2 i 2^i 2i层的祖先节点
3、打表:倍增递推
,从小到大枚举
4、查询:二进制拆分
,从大到小枚举
时间复杂度: O ( ( n + m ) ⋅ l o g n ) O((n+m)\cdot logn) O((n+m)⋅logn)
int n,m,s;
int dep[N],fa[N][20];
vector<int> e[N];
void dfs(int u,int dad){
dep[u]=dep[dad]+1;
fa[u][0]=dad;
rep(i,1,19)
fa[u][i]=fa[fa[u][i-1]][i-1];
for(int v : e[u])
if(v!=dad) dfs(v,u);
}
int lca(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
per(i,19,0)
if(dep[fa[u][i]]>=dep[v])
u=fa[u][i];
if(u==v) return v;
per(i,19,0)
if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>m>>s;//s表示树根节点
n--;
while(n--){
int a,b;
cin>>a>>b;
e[a].pb(b);
e[b].pb(a);
}
dfs(s,0);//s表示树根节点
while(m--){
int x,y;
cin>>x>>y;
cout<<lca(x,y)<<endl;
}
return 0;
}
Tarjan算法(塔杨)
结合
dfs
,遵循入、回、离原则,先路径压缩,再查询
int n,m,s;
vector<int> e[N];
vector<PII> query[N];
// vector<PII> cnt[N];
int fa[N],vis[N],ans[N];
int ax[N],bx[N];//不能直接询问
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void dfs(int u){
vis[u]=1;//标记x已访问
for(auto v : e[u]){
if(!vis[v]){
dfs(v);
fa[v]=u;//回到x时指向x
}
}
//离开x时找LCA
for(auto q : query[u]){
int v=q.fi,i=q.se;
if(vis[v]) ans[i]=find(v);
}
}
int main(){
cin>>n;
rep(i,1,N) fa[i]=i;//注意是N,而不是n
rep(i,1,n){
int a,b;
cin>>a>>b;
if(b==-1) s=a;
else{
e[a].pb(b);
e[b].pb(a);
}
}
cin>>m;
rep(i,1,m){
int a,b;
cin>>a>>b;
ax[i]=a,bx[i]=b;
query[a].pb({b,i});
query[b].pb({a,i});
// if(ans[i]==a) cout<<"1\n";
// else if(ans[i]==b) cout<<"2\n";
// else cout<<"0\n";
}
dfs(s);//查询之后再dfs
// rep(i,1,m) cout<<ans[i]<<endl;
rep(i,1,m){
if(ans[i]==ax[i]) cout<<"1\n";
else if(ans[i]==bx[i]) cout<<"2\n";
else cout<<"0\n";
}
return 0;
}
树链剖分
重链剖分
void dfs1(int u,int f){
fa[u]=f,dep[u]=dep[f]+1,siz[u]=1;
for(int v:e[u]){
if(v==f) continue;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
}
void dfs2(int u,int tp){
top[u]=tp,id[u]=++cnt,nw[cnt]=w[u];
if(!son[u]) return;
dfs2(son[u],tp);
for(int v:e[u]){
if(v==fa[u] || son[u]==v) continue;
dfs2(v,v);
}
}
#define lc u<<1
#define rc u<<1|1
int w[N],n,m,p,s;
int fa[N],son[N],dep[N],siz[N];
int cnt,id[N],nw[N],top[N];
vector<int> e[N];
struct node{
int l,r;
ll add,sum;
}tr[N*4];
void dfs1(int u,int f){
fa[u]=f,dep[u]=dep[f]+1,siz[u]=1;
for(int v:e[u]){
if(v==f) continue;//beware
dfs1(v,u);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
}
void dfs2(int u,int tp){
top[u]=tp,id[u]=++cnt,nw[cnt]=w[u];
if(!son[u]) return;
dfs2(son[u],tp);
for(int v:e[u]){
if(v==fa[u] || son[u]==v) continue;
dfs2(v,v);
}
}
void pushup(int u){
tr[u].sum=tr[lc].sum+tr[rc].sum;
}
void pushdown(int u){
if(tr[u].add){
tr[lc].sum+=tr[u].add*(tr[lc].r-tr[lc].l+1);
tr[rc].sum+=tr[u].add*(tr[rc].r-tr[rc].l+1);
tr[lc].add+=tr[u].add;
tr[rc].add+=tr[u].add;
tr[u].add=0;
}
}
void build(int u,int l,int r){
tr[u]={l,r,0,nw[r]};//beware
if(l==r) return;
int mid=l+r>>1;
build(lc,l,mid),build(rc,mid+1,r);
pushup(u);
}
void update(int u,int l,int r,int k){
if(tr[u].l>=l && tr[u].r<=r){
tr[u].add+=k,tr[u].sum+=k*(tr[u].r-tr[u].l+1);
return;
}
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(mid>=l) update(lc,l,r,k);
if(mid<r) update(rc,l,r,k);
pushup(u);
}
ll query(int u,int l,int r){
if(tr[u].l>=l && tr[u].r<=r) return tr[u].sum;
pushdown(u);
int mid =tr[u].l+tr[u].r>>1;
ll res=0;
if(mid>=l) res+=query(lc,l,r);
if(mid<r) res+=query(rc,l,r);
return res;
}
void update_path(int u,int v,int k){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
update(1,id[top[u]],id[u],k);
u=fa[top[u]];
}
if(dep[u]<dep[v]) swap(u,v);
update(1,id[v],id[u],k);
}
void update_tree(int u,int k){//修改子树
update(1,id[u],id[u]+siz[u]-1,k);
}
ll query_path(int u,int v){
ll res=0;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
res+=query(1,id[top[u]],id[u]);
u=fa[top[u]];
}
if(dep[u]<dep[v]) swap(u,v);
res+=query(1,id[v],id[u]);//最后一段
return res;
}
ll query_tree(int u){//查询子树
return query(1,id[u],id[u]+siz[u]-1);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>m>>s>>p;
rep(i,1,n) cin>>w[i];
// n--;
for(int i=0;i<n-1;i++){
int a,b;
cin>>a>>b;
e[a].pb(b),e[b].pb(a);
}
dfs1(s,0);
dfs2(s,s);//把树拆成链
build(1,1,n);//用链建`线段树`
while(m--){
int op,a,b,c;
cin>>op;
if(op==1){
cin>>a>>b>>c;
update_path(a,b,c);
}
else if(op==2){
cin>>a>>b;
cout<<query_path(a,b)%p<<endl;
}
else if(op==3){
cin>>a>>c;
update_tree(a,c);
}else{
cin>>a;
cout<<query_tree(a)%p<<endl;
}
}
return 0;
}
树上差分
Q:如果初态各点的点权不为0,如何做?
int n,m,ans;
int e[N],ne[N],h[N],idx;
int dep[N],f[N][22],power[N];
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int dad){
dep[u]=dep[dad]+1;
f[u][0]=dad;
rep(i,1,19) f[u][i]=f[f[u][i-1]][i-1];
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(v!=dad) dfs(v,u);
}
}
int lca(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
per(i,19,0)
if(dep[f[u][i]]>=dep[v])
u=f[u][i];
if(u==v) return u;
per(i,19,0)
if(f[u][i]!=f[v][i])
u=f[u][i],v=f[v][i];
return f[u][0];
}
void dfs2(int u,int dad){
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(v!=dad){
dfs2(v,u);
power[u]+=power[v];
}
}
ans=max(ans,power[u]);
}
int main(){
ms(h,-1);
cin>>n>>m;
rep(i,1,n-1){
int a,b;
cin>>a>>b;
add(a,b), add(b,a);
}
dfs(1,0);
while(m--){
int a,b;
cin>>a>>b;
int l=lca(a,b);
++power[a], ++power[b];//点:树上差分
--power[l], --power[f[l][0]];
}
dfs2(1,0);
cout<<ans;
return 0;
}
强连通分量 Tarjan 算法
入、回、离(与LCA有异曲同工之妙)
−
−
−
−
−
−
−
−
−
−
−
−
比较
−
−
−
−
−
−
−
−
−
−
−
−
\color{orange}{------------比较------------}
−−−−−−−−−−−−比较−−−−−−−−−−−−
−
−
−
−
−
−
−
−
−
−
−
−
重点
−
−
−
−
−
−
−
−
−
−
−
−
\color{orange}{------------重点------------}
−−−−−−−−−−−−重点−−−−−−−−−−−−
P2863 [USACO06JAN] The Cow Prom S
int n,m,ans;
vector<int> e[N];
int dfn[N],low[N],tot;
int stk[N],instk[N],top;
int scc[N],cnt,siz[N];
void tarjan(int x){
//入x时,盖戳、入栈
dfn[x]=low[x]=++tot;
stk[++top]=x,instk[x]=1;
for(int y : e[x]){
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);//回x时更新low
}
//若y已访问且在栈中
if(instk[y]) low[x]=min(low[x],dfn[y]);
}
//离x时,收集SCC
if(low[x]==dfn[x]){//若x是SCC的根
int y;cnt++;
do{
y=stk[top--],instk[y]=0;
scc[y]=cnt;//SCC编号
siz[cnt]++;//SCC大小
}while(x!=y);
}
}
int main(){
cin>>n>>m;
while(m--){
int a,b;
cin>>a>>b;
e[a].pb(b);
}
rep(i,1,n)
if(!dfn[i])
tarjan(i);
rep(i,1,cnt)
if(siz[i]>1) ans++;
cout<<ans;
return 0;
}
Tarjan SCC 缩点
时间复杂度:
O
(
N
+
M
)
\color{RED}{O(N+M)}
O(N+M)
P2812 校园网络
int n,a;
vector<int> e[N];
int dfn[N],low[N],tot;
int stk[N],instk[N],top;
int scc[N],cnt;
int din[N],dout[N];
void tarjan(int x){
dfn[x]=low[x]=++tot;
stk[++top]=x,instk[x]=1;
for(int y : e[x]){
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(instk[y]) low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]){
int y;cnt++;
do{
y=stk[top--], instk[y]=0;
scc[y]=cnt;
}while(x!=y);
}
}
int main(){
cin>>n;
rep(i,1,n)
while(cin>>a, a)
e[i].pb(a);
rep(i,1,n)
if(!dfn[i])
tarjan(i);
rep(x,1,n)
for(int y : e[x]){
if(scc[x]!=scc[y]){
din[scc[y]]++;
dout[scc[x]]++;
}
}
int aa=0,bb=0;
rep(i,1,cnt){
if(!din[i]) aa++;
if(!dout[i]) bb++;
}
cout<<aa<<endl;
if(cnt==1) puts("0");
else cout<<max(aa,bb)<<endl;
return 0;
}
Tarjan 割点
黑色是
时间戳
,红色是追溯值
int n,m;
vector<int> e[N];
int dfn[N],low[N],tot;
int cnt[N],s;
void tarjan(int x){
dfn[x]=low[x]=++tot;
int son=0;
for(int y : e[x]){
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){
son++;
if(x!=s || son>1) cnt[x]=1;
}
}else low[x]=min(low[x],dfn[y]);
}
}
int main(){
cin>>n>>m;
while(m--){
int a,b;
cin>>a>>b;
e[a].pb(b);
e[b].pb(a);
}
for(s=1;s<=n;s++)//s:全局变量
if(!dfn[s])
tarjan(s);
int ans=0;
rep(i,1,n)
if(cnt[i]) ans++;
cout<<ans<<endl;
rep(i,1,n)
if(cnt[i]) cout<<i<<' ';//注意审题
return 0;
}
Tarjan 割边
int n,m;
struct edge
{
int u,v;
};
vector<edge> e;
vector<int> h[N];//出边
int dfn[N],low[N],tot,cnt;
struct bright{int x,y;}bri[N];
void add(int a,int b){
e.pb({a,b});
h[a].pb(SZ(e)-1);
}
void tarjan(int x,int ed){
dfn[x]=low[x]=++tot;
for(int i=0;i<h[x].size();i++){
int j=h[x][i],y=e[j].v;
if(!dfn[y]){
tarjan(y,j);
low[x]=min(low[x],low[y]);
if(low[y]>dfn[x]) bri[cnt++]={x,y};
}
//不是反边
else if(j!=(ed^1)) low[x]=min(low[x],dfn[y]);
}
}
bool cmp(bright a,bright b){
if(a.x==b.x) return a.y<b.y;
return a.x<b.x;
}
int main(){
cin>>n>>m;
while(m--){
int a,b;
cin>>a>>b;
add(a,b);
add(b,a);
}
rep(i,1,n)
if(!dfn[i])
tarjan(i,0);
sort(bri,bri+cnt,cmp);
for(int i=0;i<cnt;i++)
cout<<bri[i].x<<' '<<bri[i].y<<endl;
return 0;
}
Tarjan三大总结
Tarjan eDCC 缩点
int n,m;
int h[N],e[N],ne[N],idx;
int dfn[N],low[N],tot;
int stk[N],top;
int dcc[N],cnt;
int bri[N],d[N];
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int x,int ed){
dfn[x]=low[x]=++tot;
stk[++top]=x;
for(int i=h[x];~i;i=ne[i]){
int y=e[i];
if(!dfn[y]){
tarjan(y,i);
low[x]=min(low[x],low[y]);
if(low[y]>dfn[x]) bri[i]=bri[i^1]=1;
}else if(i!=(ed^1)) low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
++cnt;
int y;
do{
y=stk[top--];
dcc[y]=cnt;
}while(x!=y);
}
}
int main(){
ms(h,-1);
cin>>n>>m;
while(m--){
int a,b;
cin>>a>>b;
add(a,b),add(b,a);
}
tarjan(1,-1);//题目条件是连通图
for(int i=0;i<idx;i++)//边号
if(bri[i])
d[dcc[e[i]]]++;//度数
int sum=0;
rep(i,1,cnt)
//叶子节点
if(d[i]==1) sum++;
cout<<(sum+1)/2;
return 0;
}
Tarjan vDCC 缩点
缩点总结
网络流 最大流 EK 算法
int n,m,u,S,T;
int h[N],e[N],ne[N],idx;
ll mf[N],pre[N],cp[N];
void add(int a,int b,int c){
//cp:存储容量capaci-->W[]
e[idx]=b,cp[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
bool bfs(){//找增广路
ms(mf,0);
queue<int> q;
q.push(S); mf[S]=1e9;
while(SZ(q)){
int u=q.front(); q.pop();
for(int i=h[u];~i;i=ne[i]){
ll v=e[i];
if(mf[v]==0 && cp[i]){
mf[v]=min(mf[u],cp[i]);//必须同一数据类型
pre[v]=i;//存前驱边
q.push(v);
if(v==T) return 1;
}
}
}
return 0;
}
ll EK(){//累加可行流
ll flow=0;
while(bfs()){
int v=T;
while(S!=v){//更新残留网
int i=pre[v];
cp[i]-=mf[T];
cp[i^1]+=mf[T];
v=e[i^1];
}
flow+=mf[T];
}
return flow;
}
int main(){
cin>>n>>m>>S>>T;
ms(h,-1);//-1:boundary
while(m--){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c), add(b,a,0);//反向边
}
cout<<EK();
return 0;
}
网络流 最大流 Dinic 算法
网络流 最小割 Dinic 算法
网络流 费用流 EK 算法
int n,m,S,T;
int h[N],w[N],e[N],ne[N],idx;
ll cp[N],mf[N],d[N],vis[N],pre[N];
ll flow,cost;
void add(int a,int b,int c,int d){
e[idx]=b,cp[idx]=c,w[idx]=d,ne[idx]=h[a],h[a]=idx++;
}
bool spfa(){//最短路找增广路
ms(d,0x3f);
ms(mf,0);
queue<int> q;
q.push(S);
d[S]=0,mf[S]=INF,vis[S]=1;
while(SZ(q)){
int u=q.front(); q.pop(); vis[u]=0;
for(int i=h[u];~i;i=ne[i]){
ll v=e[i], c=cp[i], we=w[i];
if(d[v]>d[u]+we && c){
d[v]=d[u]+we;//最短路
mf[v]=min(mf[u],c);
pre[v]=i;
if(!vis[v]){
q.push(v);
vis[v]=1;
}
}
}
}
return mf[T]>0;
}
void EK(){
while(spfa()){
for(int v=T;S!=v;){
int i=pre[v];
cp[i]-=mf[T];
cp[i^1]+=mf[T];
v=e[i^1];
}
flow+=mf[T];//累加可行流
cost+=mf[T]*d[T];//累加费用
}
}
int main(){
ms(h,-1);
cin>>n>>m>>S>>T;
while(m--){
int a,b,c,d;
cin>>a>>b>>c>>d;
add(a,b,c,d);
add(b,a,0,-d);
}
EK();
cout<<flow<<' '<<cost;
return 0;
}
二分图判定 染色法
int n,m;
int h[N],e[N],ne[N],idx;
int flag,color[N];
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool dfs(int u,int c){
color[u]=c;
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(!color[v]){//这里一定要加个{}
if(dfs(v,3-c)) return 1;
}
else if(color[v]==c) return 1;
}
return 0;
}
int main(){
ms(h,-1);
cin>>n>>m;
while(m--){
int a,b;
cin>>a>>b;
add(a,b),add(b,a);
}
rep(i,1,n){
if(!color[i])
if(dfs(i,1)){
flag=1;
break;
}
}
if(flag) cout<<"No";
else cout<<"Yes";
}
二分图最大匹配 匈牙利算法
int n,m,a,b,k,ans;
int h[N],e[N],ne[N],idx;
int match[N],vis[N];
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool dfs(int u){
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(vis[v]) continue;
vis[v]=1;
if(!match[v] || dfs(match[v])){
match[v]=u;
return 1;
}
}
return 0;
}
int main(){
ms(h,-1);
cin>>n>>m>>k;
while(k--){
cin>>a>>b;
add(a,b);
}
rep(i,1,n){
ms(vis,0);
if(dfs(i)) ans++;
}
cout<<ans;
return 0;
}
二分图最大匹配 Dinic算法
二分图最大权完美匹配 KM算法
- 由匈牙利算法演化而来
- 思考:如何优化
dfs
为bfs
,使得复杂度降为 0 ( n 3 ) 0(n^3) 0(n3)
基环树
int n,m,r1,r2;
int e[N],ne[N],h[N],idx;
int w[N],vis[N];
ll f[N][2];
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void find(int u,int rt){//找两个根
vis[u]=1;
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(v==rt){r1=u,r2=v;return;}
if(vis[v]) continue;
find(v,rt);
}
}
ll dfs(int u,int rt){//树上DP
f[u][0]=0, f[u][1]=w[u];
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(v==rt) continue;
dfs(v,rt);
f[u][0]+=max(f[v][0],f[v][1]);
f[u][1]+=f[v][0];
}
return f[u][0];
}
int main(){
ms(h,-1);
cin>>n;
for(int i=1,u;i<=n;i++){
cin>>w[i]>>u;
add(u,i);
}
ll sum=0;
rep(i,1,n){
if(!vis[i]){
r1=r2=0;//环上的起点和终点
find(i,i);
if(r1){
ll res1=dfs(r1,r1);
ll res2=dfs(r2,r2);
sum+=max(res1,res2);
}
}
}
cout<<sum;
return 0;
}
int n,m;
vector<int> e[N];
vector<int> path(N,N);//
PII edge[N];
int du,dv,vis[N];//断边的两个点
int cnt,bt;
bool dfs(int u){
if(!bt){
if(u>path[cnt]) return 1;
if(u<path[cnt]) bt=-1;
}
vis[u]=1;
path[cnt++]=u;
for(auto x : e[u]){
int v=x;
if(vis[v]) continue;
if(du==u && dv==v) continue;
if(du==v && dv==u) continue;
if(dfs(v)) return 1;
}
return 0;
}
int main(){
cin>>n>>m;
rep(i,1,m){
int a,b;
cin>>a>>b;
e[a].eb(b);
e[b].eb(a);
edge[i]={a,b};
}
rep(i,1,n) sort(all(e[i]));
if(n==m+1) dfs(1);
else{
rep(i,1,m){
du=edge[i].fi;
dv=edge[i].se;
ms(vis,0);
cnt=bt=0;
dfs(1);
}
}
rep(i,0,n-1) cout<<path[i]<<' ';
return 0;
}
圆方树 【模板】静态仙人掌
仙人掌: 环与环之间不存在公共边