CF网络流练习

Posted uid001

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF网络流练习相关的知识,希望对你有一定的参考价值。

103E

大意: 给定$n$个集合, 满足对于任意的$k$, 任意$k$个集合的并集都不少于$k$. 要求选出$k$个集合(k\\ge 0), 使得并恰好等于$k$, 输出最少花费.

Hall定理: 二分图的$X$部到$Y$部有完美匹配等价于$X$中任意$k$个点与$Y$中至少$k$个点相邻.

所有集合为$X$部, 每个数为$Y$部, 集合向所含数连边, 那么一定存在完美匹配. 假设求出一组匹配, 数字$i$对应集合$C_i$, 那么最终若选取集合$A_i$, 则$A_i$中所有数字$x$对应的集合$C_x$一定也要选, 把权值取负就转化为最大权闭合子图模型. 对于最大权闭合子图问题, 源点连所有正权点, 容量为该点权值, 所有负权点连汇点, 容量为该点权值的绝对值, 其余边与原图一样, 容量为无穷, 求出源点到汇点的最小割, 那么答案为正权和-最小割, 与源点间的割的含义为不选择该点, 与汇点间的割的含义为选择该点.

技术图片
#include <iostream>
#include <cstdio>
#include <queue>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define pb push_back
using namespace std;

const int N = 1e6+10, INF = 0x3f3f3f3f;
int n, S, T, clk, f[N], vis[N];
vector<int> g[N];
int dfs(int x) 
    for (int y:g[x]) if (vis[y]!=clk) 
        vis[y] = clk;
        if (!f[y]||dfs(f[y])) return f[y]=x;
    
    return 0;

struct edge 
    int v,w,next;
 e[N];
int head[N], dep[N], cur[N], cnt=1;
queue<int> Q;
void add(int u, int v, int w) 
    e[++cnt] = v,w,head[u];
    head[u] = cnt;
    e[++cnt] = u,0,head[v];
    head[v] = cnt;

int bfs() 
    REP(i,1,T) dep[i]=INF,vis[i]=0,cur[i]=head[i];
    dep[S]=0,Q.push(S);
    while (Q.size()) 
        int u = Q.front(); Q.pop();
        for (int i=head[u]; i; i=e[i].next) 
            if (dep[e[i].v]>dep[u]+1&&e[i].w) 
                dep[e[i].v]=dep[u]+1;
                Q.push(e[i].v);
            
        
    
    return dep[T]!=INF;

int dfs(int x, int w) 
    if (x==T) return w;
    int used = 0;
    for (int i=cur[x]; i; i=e[i].next) 
        cur[x] = i;
        if (dep[e[i].v]==dep[x]+1&&e[i].w) 
            int flow = dfs(e[i].v,min(w-used,e[i].w));
            if (flow) 
                used += flow;
                e[i].w -= flow;
                e[i^1].w += flow;
                if (used==w) break;
            
        
    
    return used;

int dinic() 
    int ans = 0;
    while (bfs()) ans+=dfs(S,INF);
    return ans;



int main() 
    scanf("%d", &n), S = n+1, T = S+1;
    REP(i,1,n) 
        int k, t;
        scanf("%d", &k);
        while (k--) 
            scanf("%d", &t);
            g[i].pb(t+n);
            g[t+n].pb(i);
        
    
    REP(i,1,n) ++clk, dfs(i);
    int sum = 0;
    REP(i,1,n) 
        int t;
        scanf("%d", &t);
        t = -t;
        if (t>=0) add(S,i,t),sum+=t;
        else add(i,T,-t);
    
    REP(i,1,n) for (int j:g[i]) add(i,f[j],INF);
    printf("%d\\n", dinic()-sum);
View Code

164C

大意: $k$台机器, $n$个任务, 任务$i$开始时间$s_i$, 持续时间$t_i$, 获利$c_i$, 每台机器可以处理任何任务, 没处理完不能切换, 求完成哪些任务收益最大.

最大$k$重区间集问题, 时间离散化, 每个时间向下一个时间连容量$k$, 花费$0$, 任务$i$起点向终点连容量$1$, 花费$c_i$, 求出最大费用最大流即可.

技术图片
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;

const int N = 5e4+10,S=N-2,T=N-1,INF=0x3f3f3f3f;
int n,m,cost,flow,b[N];
struct edge 
    int to,next,w,v;
    edge(int to=0,int next=0,int w=0,int v=0):to(to),next(next),w(w),v(v)
 e[N];
int head[N],dep[N],vis[N],cnt=1;
queue<int> Q;

int spfa() 
    REP(i,1,*b) dep[i]=-INF,vis[i]=0;
    dep[T]=-INF;
    dep[S]=0,Q.push(S);
    while (Q.size()) 
        int u = Q.front(); Q.pop();
        vis[u] = 0;
        for (int i=head[u]; i; i=e[i].next) 
            if (e[i].w&&dep[e[i].to]<dep[u]+e[i].v) 
                dep[e[i].to]=dep[u]+e[i].v;
                if (!vis[e[i].to])  
                    vis[e[i].to]=1;
                    Q.push(e[i].to);
                
            
        
    
    return dep[T]!=-INF;

int dfs(int x, int w) 
    if (x==T) 
        cost = cost+w*dep[T];
        flow += w;
        return w;
    
    vis[x] = 1;
    int used = 0;
    for (int i=head[x]; i; i=e[i].next) 
        if (!vis[e[i].to]&&dep[e[i].to]==dep[x]+e[i].v&&e[i].w) 
            int f = dfs(e[i].to,min(w-used,e[i].w));
            if (f) 
                used += f;
                e[i].w -= f;
                e[i^1].w += f;
                if(used==w) break;
            
        
    
    return used;

void dinic()
    while(spfa()) dfs(S,INF);

void add(int x,int y,int k,int v) 
    e[++cnt] = edge(y,head[x],k,v);
    head[x] = cnt;
    e[++cnt] = edge(x,head[y],0,-v);
    head[y] = cnt;


int k,L[N],R[N],c[N],no[N];
int main() 
    scanf("%d%d", &n, &k);
    REP(i,1,n) 
        int s,t;
        scanf("%d%d%d",&s,&t,c+i);
        b[++*b]=L[i]=s;
        b[++*b]=R[i]=s+t;
    
    sort(b+1,b+1+*b),*b=unique(b+1,b+1+*b)-b-1;
    REP(i,1,n)  
        L[i]=lower_bound(b+1,b+1+*b,L[i])-b;
        R[i]=lower_bound(b+1,b+1+*b,R[i])-b;
    
    add(S,1,k,0),add(*b,T,k,0);
    REP(i,2,*b) add(i-1,i,k,0);
    REP(i,1,n) no[i]=cnt+1,add(L[i],R[i],1,c[i]);
    dinic();
    REP(i,1,n) printf("%d ", !e[no[i]].w);
    puts("");
View Code

237E

大意: 给定字符串$t$, 给定$n$个子串, 第$i$个子串$s_i$最多可以选出$a_i$个字符, 每个字符花费为$i$, 求组成字符串$t$的最少花费.

裸的费用流, 源点向子串$i$连容量$a_i$, 费用$0$, $i$向每个字符$x$连容量$cnt_s[x]$, 费用$i$, 最后每个字符$x$向汇点连容量$cnt_t[x]$, 费用0. 求最小费用即可.

技术图片
#include <iostream>
#include <cstdio>
#include <queue>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;

const int N = 5e4+10,S=N-2,T=N-1,INF=0x3f3f3f3f;
int n,m,cost,flow,b[N];
struct edge 
    int to,next,w,v;
    edge(int to=0,int next=0,int w=0,int v=0):to(to),next(next),w(w),v(v)
 e[N];
int head[N],dep[N],vis[N],cnt=1;
queue<int> Q;

int spfa() 
    REP(i,1,n+30) dep[i]=INF,vis[i]=0;
    dep[T]=INF;
    dep[S]=0,Q.push(S);
    while (Q.size()) 
        int u = Q.front(); Q.pop();
        vis[u] = 0;
        for (int i=head[u]; i; i=e[i].next) 
            if (e[i].w&&dep[e[i].to]>dep[u]+e[i].v) 
                dep[e[i].to]=dep[u]+e[i].v;
                if (!vis[e[i].to]) 
                    vis[e[i].to]=1;
                    Q.push(e[i].to);
                
            
        
    
    return dep[T]!=INF;

int dfs(int x, int w) 
    if (x==T) 
        cost = cost+w*dep[T];
        flow += w;
        return w;
    
    vis[x] = 1;
    int used = 0;
    for (int i=head[x]; i; i=e[i].next) 
        if (!vis[e[i].to]&&dep[e[i].to]==dep[x]+e[i].v&&e[i].w) 
            int f = dfs(e[i].to,min(w-used,e[i].w));
            if (f) 
                used += f;
                e[i].w -= f;
                e[i^1].w += f;
                if(used==w) break;
            
        
    
    return used;

void dinic()
    while(spfa()) dfs(S,INF);

void add(int x,int y,int k,int v) 
    e[++cnt] = edge(y,head[x],k,v);
    head[x] = cnt;
    e[++cnt] = edge(x,head[y],0,-v);
    head[y] = cnt;


int f[N];
char s[N];

int main() 
    scanf("%s%d", s+1, &n);
    m = strlen(s+1);
    REP(i,1,m) ++f[s[i]];
    int sum = 0;
    REP(i,a,z) if (f[i])  
        add(n+i-a+1,T,f[i],0),sum+=f[i],f[i]=0;
    
    REP(i,1,n) 
        int x;
        scanf("%s%d", s+1, &x);
        m = strlen(s+1);
        REP(i,1,m) ++f[s[i]];
        add(S,i,x,0);
        REP(j,a,z) if (f[j]) add(i,n+j-a+1,f[j],i);
        REP(i,a,z) f[i]=0;
    
    dinic();
    if (flow!=sum) return puts("-1"),0;
    printf("%d\\n", cost);
View Code

269C

大意:给定无向图, 求将边定向, 使它成为一个$1$到$n$的流.

拓扑排序即可. 

技术图片
#include <iostream>
#include <cstdio>
#include <queue>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define pb push_back
using namespace std;


const int N = 1e6+10;
int n,m,deg[N],ans[N],vis[N];
struct _ int to,w,id;;
vector<_> g[N];

int main() 
    scanf("%d%d", &n, &m);
    REP(i,1,m) 
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        g[u].pb(v,w,i);
        g[v].pb(u,w,-i);
        deg[u]+=w,deg[v]+=w;
    
    queue<int> q;
    q.push(1),vis[n]=1;
    memset(ans,-1,sizeof ans);
    while (q.size()) 
        int x = q.front(); q.pop();
        vis[x] = 1;
        for (_ e:g[x])  
            if (e.id<0) 
                if (ans[-e.id]==-1) ans[-e.id]=1;
            
            else if (ans[e.id]==-1) ans[e.id]=0;
            if (!vis[e.to]) 
                if (!(deg[e.to]-=2*e.w))  
                    q.push(e.to);
                
            
        
    
    REP(i,1,m) printf("%d\\n",ans[i]);
View Code

277E

大意: 给定$n$个平面点, $y$值大的可以向$y$值小的连有向边, 求一棵边权和最小的有根二叉树.

每个点入度1, 出度不超过2, 建图跑费用流即可.

技术图片
#include <iostream>
#include <cstdio>
#include <queue>
#include <math.h>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define pb push_back
using namespace std;

const int N = 1e5+10, INF = 0x3f3f3f3f;
int n, m, k;
struct _ int from,to,w;double f;;
vector<_> E;
vector<int> g[N];
int a[N], pre[N], inq[N];
double d[N];
int mf;
double mc;
queue<int> q;
void add(int x, int y, int c, double w) 
    g[x].pb(E.size());
    E.pb(x,y,c,w);
    g[y].pb(E.size());
    E.pb(y,x,0,-w);

void mfmc(int S, int T) 
    while (1) 
        REP(i,1,T) a[i]=INF,d[i]=1e15,inq[i]=0;
        q.push(S),d[S]=0;
        while (!q.empty()) 
            int x=q.front(); q.pop();
            inq[x] = 0;
            for (auto t:g[x]) 
                auto e=E[t];
                if (e.w>0&&d[e.to]>d[x]+e.f) 
                    d[e.to]=d[x]+e.f;
                    pre[e.to]=t;
                    a[e.to]=min(a[x],e.w);
                    if (!inq[e.to]) 
                        inq[e.to]=1;
                        q.push(e.to);
                    
                
            
        
        if (a[T]==INF) break;
        for (int u=T;u!=S;u=E[pre[u]].from) 
            E[pre[u]].w-=a[T];
            E[pre[u]^1].w+=a[T];
        
        mf+=a[T],mc+=a[T]*d[T];
    


int x[N],y[N];
int main() 
    scanf("%d", &n);
    int S=2*n+1,T=S+1;
    REP(i,1,n)  
        scanf("%d%d",x+i,y+i);
        add(S,i,2,0);
        add(i+n,T,1,0);
    
    REP(i,1,n) REP(j,1,n) if (y[i]>y[j])  
        add(i,j+n,1,sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])));
    
    mfmc(S,T);
    if (mf!=n-1) return puts("-1"),0;
    printf("%.10lf\\n",mc);
View Code

311E

大意: $n$个点, 颜色黑或白, 翻转第$i$个点的颜色花费$v_i$, $m$个需求, 要求一些点全白或全黑, 满足则有一定收益. 有些特殊需求若不满足则要花费$g$, 求最大收益.

最大权闭合子图问题, 最大收益转为总收益减去最小割. $S$连黑点和黑需求, 白点和白需求连$T$, 再对每个需求关系连一下边即可.

技术图片
#include <iostream>
#include <cstdio>
#include <queue>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;

const int N = 1e6+10, S = N-2, T = N-1, INF = 0x3f3f3f3f;
int n, m, g, a[N], b[N];
struct edge 
    int to,w,next;
    edge(int to=0,int w=0,int next=0):to(to),w(w),next(next)
 e[N];
int head[N], dep[N], vis[N], cur[N], cnt=1;
queue<int> Q;
void add(int u, int v, int w) 
    e[++cnt] = edge(v,w,head[u]);
    head[u] = cnt;
    e[++cnt] = edge(u,0,head[v]);
    head[v] = cnt;

int bfs() 
    REP(i,1,n+m) dep[i]=INF,vis[i]=0,cur[i]=head[i];
    dep[S]=INF,vis[S]=0,cur[S]=head[S];
    dep[T]=INF,vis[T]=0,cur[T]=head[T];
    dep[S]=0,Q.push(S);
    while (Q.size()) 
        int u = Q.front(); Q.pop();
        for (int i=head[u]; i; i=e[i].next) 
            if (dep[e[i].to]>dep[u]+1&&e[i].w) 
                dep[e[i].to]=dep[u]+1;
                Q.push(e[i].to);
            
        
    
    return dep[T]!=INF;

int dfs(int x, int w) 
    if (x==T) return w;
    int used = 0;
    for (int i=cur[x]; i; i=e[i].next) 
        cur[x] = i;
        if (dep[e[i].to]==dep[x]+1&&e[i].w) 
            int f = dfs(e[i].to,min(w-used,e[i].w));
            if (f) used+=f,e[i].w-=f,e[i^1].w+=f;
            if (used==w) break;
        
    
    return used;

int dinic() 
    int ans = 0;
    while (bfs()) ans+=dfs(S,INF);
    return ans;


int main() 
    scanf("%d%d%d", &n, &m, &g);
    REP(i,1,n) scanf("%d",a+i);
    REP(i,1,n) scanf("%d",b+i);
    REP(i,1,n) 
        if (a[i]) add(S,i,b[i]);
        else add(i,T,b[i]);
    
    int sum = 0;
    REP(i,1,m) 
        int c,w,k,t,f;
        scanf("%d%d%d",&c,&w,&k);
        sum += w;
        while (k--) 
            scanf("%d", &t);
            if (c) add(i+n,t,INF);
            else add(t,i+n,INF);
        
        scanf("%d", &f);
        if (f) f = g;
        if (c) add(S,i+n,f+w);
        else add(i+n,T,f+w);
    
    printf("%d\\n",sum-dinic());
View Code

316C

大意: 给定$n*m$矩阵, $n*m$为偶数, $[1,\\fracnm2]$每个数均出现$2$次, 求最少交换数使得相同数字相邻.

二分图最佳完美匹配, 每个点向相邻格子连边, 同色连费用0, 异色连费用1, 求出费用最小的完美匹配即为答案

技术图片
#include <iostream>
#include <queue>
#define REP(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
const int N = 1e6+10, S = N-2, T = N-1, INF = 0x3f3f3f3f;
int n, m, flow, cost;
struct edge 
    int to,w,v,next;
    edge(int to=0,int w=0,int v=0,int next=0):to(to),v(v),w(w),next(next)
 e[N];
int head[N], dep[N], vis[N], cur[N], f[N], cnt=1;
pair<int,int> pre[N];
queue<int> Q;
void add(int u, int v, int w, int k) 
    e[++cnt] = edge(v,w,k,head[u]);
    head[u] = cnt;
    e[++cnt] = edge(u,0,-k,head[v]);
    head[v] = cnt;

int spfa() 
    REP(i,1,n*m) f[i]=dep[i]=INF,vis[i]=0;
    f[S]=dep[S]=f[T]=dep[T]=INF;
    dep[S]=0,Q.push(S);
    while (Q.size()) 
        int u = Q.front(); Q.pop();
        vis[u] = 0;
        for (int i=head[u]; i; i=e[i].next) 
            if (dep[e[i].to]>dep[u]+e[i].v&&e[i].w) 
                dep[e[i].to]=dep[u]+e[i].v;
                pre[e[i].to]=pair<int,int>(u,i);
                f[e[i].to]=min(f[u],e[i].w);
                if (!vis[e[i].to]) 
                    vis[e[i].to]=1;
                    Q.push(e[i].to);
                
            
        
    
    return dep[T]!=INF;

int dfs(int x, int w) 
    if (x==T) return w;
    int used = 0;
    for (int i=cur[x]; i; i=e[i].next) 
        cur[x] = i;
        if (dep[e[i].to]==dep[x]+1&&e[i].w) 
            int f = dfs(e[i].to,min(w-used,e[i].w));
            if (f) used+=f,e[i].w-=f,e[i^1].w+=f;
            if (used==w) break;
        
    
    return used;

void EK()
    while(spfa()) 
        int w = f[T];
        for (int u=T; u!=S; u=pre[u].first) 
            e[pre[u].second].w-=w;
            e[pre[u].second^1].w+=w;
        
        flow += w, cost += w*dep[T];
    

int a[99][99];
const int dx[]=0,0,-1,1;
const int dy[]=-1,1,0,0;
int ID(int x, int y) 
    return (x-1)*m+y;

int main() 
    scanf("%d%d", &n, &m);
    REP(i,1,n) REP(j,1,m) scanf("%d",a[i]+j);
    REP(i,1,n) REP(j,1,m) 
        if (i!=n) 
            int x=ID(i,j),y=ID(i+1,j);
            if (i+j&1) swap(x,y);
            add(x,y,1,a[i][j]!=a[i+1][j]);
        
        if (j!=m) 
            int x=ID(i,j),y=ID(i,j+1);
            if (i+j&1) swap(x,y);
            add(x,y,1,a[i][j]!=a[i][j+1]);
        
        if (i+j&1) add(ID(i,j),T,1,0);
        else add(S,ID(i,j),1,0);
    
    EK();
    printf("%d\\n",cost);
View Code

321B

大意: 对手$n$只怪, 你有$m$只怪, 现在是你的回合. 对面怪全有嘲讽, 分为攻击怪和防御怪. 若你攻击对面攻击怪, 要满足你怪的能力不少于对面, 攻击后对面怪死亡, 对英雄伤害为能力差. 若你攻击对面防御怪, 要满足你怪的能力大于对面, 攻击后对面怪死亡, 对英雄伤害$0$. 若对面没怪可以直接攻击对面英雄, 伤害为你的怪的能力值. 求英雄造成的最大伤害.

不攻击英雄的情况贪心判掉. 攻击英雄的情况, 显然是一个最大带权匹配问题, 用费用流或者$KM$即可.

技术图片
#include <iostream>
#include <cstdio>
#include <queue>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
const int N = 1e6+10, INF = 0x3f3f3f3f, S = N-2, T = N-1;
int n, m, flow, cost;
struct edge 
    int to,w,v,next;
    edge(int to=0,int w=0,int v=0,int next=0):to(to),w(w),v(v),next(next)
 e[N];
int head[N], dep[N], vis[N], cur[N], f[N], cnt=1;
int pre[N],pre2[N];
queue<int> Q;
void add(int u, int v, int w, int k) 
    e[++cnt] = edge(v,w,k,head[u]);
    head[u] = cnt;
    e[++cnt] = edge(u,0,-k,head[v]);
    head[v] = cnt;

int spfa() 
    REP(i,1,n+m) f[i]=dep[i]=INF,vis[i]=0;
    f[S]=dep[S]=f[T]=dep[T]=INF;
    dep[S]=0,Q.push(S);
    while (Q.size()) 
        int u = Q.front(); Q.pop();
        vis[u] = 0;
        for (int i=head[u]; i; i=e[i].next) 
            if (dep[e[i].to]>dep[u]+e[i].v&&e[i].w) 
                dep[e[i].to]=dep[u]+e[i].v;
                pre[e[i].to]=u,pre2[e[i].to]=i;
                f[e[i].to]=min(f[u],e[i].w);
                if (!vis[e[i].to]) 
                    vis[e[i].to]=1;
                    Q.push(e[i].to);
                
            
        
    
    return dep[T]!=INF;

void EK()
    while(spfa()) 
        int w = f[T];
        for (int u=T; u!=S; u=pre[u]) 
            e[pre2[u]].w-=w;
            e[pre2[u]^1].w+=w;
        
        flow += w, cost += w*dep[T];
    

int a[N], b[N], c[N];
char s[110][10];

int main() 
    scanf("%d%d", &n, &m);
    REP(i,1,n) scanf("%s%d",s[i],a+i);
    int sum = 0;
    REP(i,1,m) scanf("%d",b+i),sum+=b[i];
    REP(i,1,m) add(S,i,1,0);
    REP(i,1,n) add(i+m,T,1,0);
    REP(i,1,m) REP(j,1,n) 
        if (s[j][0]==A) 
            if (b[i]>=a[j]) add(i,j+m,1,a[j]);
            else add(i,j+m,1,1e6);
        
        else 
            if (b[i]>a[j]) add(i,j+m,1,b[i]);
            else add(i,j+m,1,1e6);
        
    
    EK();
    int ans = sum-cost;
    REP(i,1,n) if (s[i][0]==A) c[++*c]=a[i];
    sort(b+1,b+1+m,greater<int>());
    sort(c+1,c+1+*c);
    int ret = 0;
    REP(i,1,min(m,*c)) ret += max(0,b[i]-c[i]);
    printf("%d\\n",max(ans,ret));
View Code 

343E

最小割树

362E

大意: 给定$n$个点的网络图, 每次操作选一条边流量$+1$, 最多$k$次, 求最大流.

先求一次最大流, 然后对残量网络上每条边加一条容量$k$, 费用为$1$的边, 再跑一次费用流, 当费用达到$k$时停止.

技术图片
#include <iostream>
#include <cstdio>
#include <queue>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef pair<int,int> pii;
const int N = 5e4+10,INF=0x3f3f3f3f;
int n,m,cost,flow,S,T;
struct edge 
    int to,next,w,v;
    edge(int to=0,int next=0,int w=0,int v=0):to(to),next(next),w(w),v(v)
 e[N];
int head[N],dep[N],vis[N],f[N],cnt=1;
pii pre[N];
queue<int> Q;

int spfa() 
    REP(i,1,n) f[i]=dep[i]=INF,vis[i]=0;
    dep[S]=0,Q.push(S);
    while (Q.size()) 
        int u = Q.front(); Q.pop();
        vis[u] = 0;
        for (int i=head[u]; i; i=e[i].next) 
            if (e[i].w&&dep[e[i].to]>dep[u]+e[i].v) 
                dep[e[i].to]=dep[u]+e[i].v;
                pre[e[i].to]=pii(u,i);
                f[e[i].to]=min(f[u],e[i].w);
                if (!vis[e[i].to])  
                    vis[e[i].to]=1;
                    Q.push(e[i].to);
                
            
        
    
    return cost+dep[T]<=m;

void EK()
    while(spfa())  
        int w = f[T];
        if (cost+(long long)w*dep[T]>m) w = (m-cost)/dep[T];
        for (int u=T; u!=S; u=pre[u].first) 
            e[pre[u].second].w-=w;
            e[pre[u].second^1].w+=w;
        
        flow += w, cost += w*dep[T];
    

void add(int x,int y,int k,int v) 
    e[++cnt] = edge(y,head[x],k,v);
    head[x] = cnt;
    e[++cnt] = edge(x,head[y],0,-v);
    head[y] = cnt;


int a[66][66];
int main() 
    scanf("%d%d", &n, &m);
    S=1,T=n;
    REP(i,1,n) REP(j,1,n) 
        scanf("%d", a[i]+j);
        if (a[i][j]) add(i,j,a[i][j],0);
    
    EK();
    REP(i,1,n) REP(j,1,n) 
        if (a[i][j]) add(i,j,m,1);
    
    EK();
    printf("%d\\n", flow);
View Code

434D

https://www.cnblogs.com/Skyminer/p/6337959.html

491C

大意:给定两个长为$n$的串, 求改变第一个字符串的字母映射关系, 使得两个串对应位置相等的个数最大.

裸的最大带权匹配.

技术图片
#include <iostream>
#include <cstdio>
#include <queue>
#include <map>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
const int N = 2e6+10, INF = 0x3f3f3f3f, S = N-2, T = N-1;
int flow, cost, k, n;
struct edge 
    int to,w,v,next;
    edge(int to=0,int w=0,int v=0,int next=0):to(to),w(w),v(v),next(next)
 e[N];
int head[N], dep[N], vis[N], cur[N], f[N], cnt=1;
int pre[N],pre2[N];
queue<int> Q;
int spfa() 
    REP(i,1,2*k) f[i]=dep[i]=INF,vis[i]=0;
    f[S]=dep[S]=f[T]=dep[T]=INF;
    dep[S]=0,Q.push(S);
    while (Q.size()) 
        int u = Q.front(); Q.pop();
        vis[u] = 0;
        for (int i=head[u]; i; i=e[i].next) 
            if (dep[e[i].to]>dep[u]+e[i].v&&e[i].w) 
                dep[e[i].to]=dep[u]+e[i].v;
                pre[e[i].to]=u,pre2[e[i].to]=i;
                f[e[i].to]=min(f[u],e[i].w);
                if (!vis[e[i].to]) 
                    vis[e[i].to]=1;
                    Q.push(e[i].to);
                
            
        
    
    return dep[T]!=INF;

void EK()
    while(spfa()) 
        int w = f[T];
        for (int u=T; u!=S; u=pre[u]) 
            e[pre2[u]].w-=w;
            e[pre2[u]^1].w+=w;
        
        flow += w, cost += w*dep[T];
    

void add(int u, int v, int w, int k) 
    e[++cnt] = edge(v,w,k,head[u]);
    head[u] = cnt;
    e[++cnt] = edge(u,0,-k,head[v]);
    head[v] = cnt;


int a[111][111],b[111][111],ans[N],ID[N];
char s[N],ss[N],t[N],val[N];

int main() 
    scanf("%d%d", &n, &k);
    REP(i,1,min(26,k)) ID[val[i]=i-1+a]=i;
    REP(i,27,k) ID[val[i]=i-27+A]=i;
    scanf("%s%s",s+1,ss+1);
    REP(i,1,n) ++a[ID[s[i]]][ID[ss[i]]];
    REP(i,1,k) add(S,i,1,0),add(i+k,T,1,0);
    REP(i,1,k) REP(j,1,k) b[i][j]=cnt+1,add(i,j+k,1,-a[i][j]);
    EK();
    printf("%d\\n", -cost);
    REP(i,1,k) REP(j,1,k) if (!e[b[i][j]].w) ans[i]=val[j];
    REP(i,1,k) putchar(ans[i]);puts("");
View Code 

498C

大意: 给定序列$a$, $m$个二元组$(x,y)$, 保证$x+y$为奇数, 每次操作任选一个二元组$(x,y)$, 选择一个$k>1$, 且$k$能整除$a_x$和$a_y$, 然后将$a_x,a_y$除以$k$, 求最多进行多少次操作.

裸的最大流.

技术图片
#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
#include <queue>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define x first
#define y second
using namespace std;
typedef pair<int,int> pii;
inline int rd() int x=0;char p=getchar();while(p<0||p>9)p=getchar();while(p>=0&&p<=9)x=x*10+p-0,p=getchar();return x;

const int N = 1e6+10, S = N-2, T = N-1, INF = 0x3f3f3f3f;
int n,m,tot;
struct edge 
    int to,w,next;
    edge(int to=0,int w=0,int next=0):to(to),w(w),next(next)
 e[N];
int head[N], dep[N], vis[N], cur[N], cnt=1;
queue<int> Q;
int bfs() 
    REP(i,1,tot) dep[i]=INF,vis[i]=0,cur[i]=head[i];
    dep[S]=INF,vis[S]=0,cur[S]=head[S];
    dep[T]=INF,vis[T]=0,cur[T]=head[T];
    dep[S]=0,Q.push(S);
    while (Q.size()) 
        int u = Q.front(); Q.pop();
        for (int i=head[u]; i; i=e[i].next) 
            if (dep[e[i].to]>dep[u]+1&&e[i].w) 
                dep[e[i].to]=dep[u]+1;
                Q.push(e[i].to);
            
        
    
    return dep[T]!=INF;

int dfs(int x, int w) 
    if (x==T) return w;
    int used = 0;
    for (int i=cur[x]; i; i=e[i].next) 
        cur[x] = i;
        if (dep[e[i].to]==dep[x]+1&&e[i].w) 
            int f = dfs(e[i].to,min(w-used,e[i].w));
            if (f) used+=f,e[i].w-=f,e[i^1].w+=f;
            if (used==w) break;
        
    
    return used;

int dinic() 
    int ans = 0;
    while (bfs()) ans+=dfs(S,INF);
    return ans;

void add(int u, int v, int w) 
    e[++cnt] = edge(v,w,head[u]);
    head[u] = cnt;
    e[++cnt] = edge(u,0,head[v]);
    head[v] = cnt;

map<int,int> f[N];
map<pii,int> mp;
int ID(int x, int y) 
    if (mp.count(pii(x,y))) return mp[pii(x,y)];
    return mp[pii(x,y)] = ++tot;

map<int,int> fac(int x) 
    int mx = sqrt(x+0.5);
    map<int,int> v;
    REP(i,2,mx) 
        while (x%i==0) x/=i,++v[i];
    
    if (x>1) ++v[x];
    return v;


int main() 
    scanf("%d%d", &n, &m);
    REP(i,1,n)  
        f[i]=fac(rd());
        if (i&1) for (auto t:f[i]) add(ID(i,t.x),T,t.y);
        else for (auto t:f[i]) add(S,ID(i,t.x),t.y);
    
    REP(i,1,m) 
        int u=rd(),v=rd();
        if (u&1) swap(u,v);
        for (auto t:f[u]) 
            if (f[v].count(t.x)) add(ID(u,t.x),ID(v,t.x),INF);
        
    
    printf("%d\\n", dinic());
View Code

 510E

大意: n只狐狸, 要求分成若干个环, 每个环的狐狸不少于三只, 相邻狐狸年龄和为素数.

年龄大于$1$, 那么两个数和为素数必然是一奇一偶, 奇偶分开建图跑最大流即可.

技术图片
#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
#include <queue>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define pb push_back
#define x first
#define y second
using namespace std;
typedef pair<int,int> pii;
inline int rd() int x=0;char p=getchar();while(p<0||p>9)p=getchar();while(p>=0&&p<=9)x=x*10+p-0,p=getchar();return x;

const int N = 1e6+10, S = N-2, T = N-1, INF = 0x3f3f3f3f;
int n;
struct edge 
    int to,w,next;
    edge(int to=0,int w=0,int next=0):to(to),w(w),next(next)
 e[N];
int head[N], dep[N], vis[N], cur[N], cnt=1;
queue<int> Q;
int bfs() 
    REP(i,1,n) dep[i]=INF,vis[i]=0,cur[i]=head[i];
    dep[S]=INF,vis[S]=0,cur[S]=head[S];
    dep[T]=INF,vis[T]=0,cur[T]=head[T];
    dep[S]=0,Q.push(S);
    while (Q.size()) 
        int u = Q.front(); Q.pop();
        for (int i=head[u]; i; i=e[i].next) 
            if (dep[e[i].to]>dep[u]+1&&e[i].w) 
                dep[e[i].to]=dep[u]+1;
                Q.push(e[i].to);
            
        
    
    return dep[T]!=INF;

int dfs(int x, int w) 
    if (x==T) return w;
    int used = 0;
    for (int i=cur[x]; i; i=e[i].next) 
        cur[x] = i;
        if (dep[e[i].to]==dep[x]+1&&e[i].w) 
            int f = dfs(e[i].to,min(w-used,e[i].w));
            if (f) used+=f,e[i].w-=f,e[i^1].w+=f;
            if (used==w) break;
        
    
    return used;

int dinic() 
    int ans = 0;
    while (bfs()) ans+=dfs(S,INF);
    return ans;

void add(int u, int v, int w) 
    e[++cnt] = edge(v,w,head[u]);
    head[u] = cnt;
    e[++cnt] = edge(u,0,head[v]);
    head[v] = cnt;


int f[N],a[N];
vector<int> g[N],ans[N];
void seive(int n) 
    int mx = sqrt(n+0.5);
    REP(i,2,mx) if (!f[i]) 
        for (int j=i*i; j<=n; j+=i) f[j] = 1;
    


int main() 
    seive(20100);
    scanf("%d", &n);
    REP(i,1,n)  
        if ((a[i]=rd())&1) add(S,i,2);
        else add(i,T,2);
    
    REP(i,1,n) if (a[i]&1) REP(j,1,n) if (a[j]&1^1) 
        if (!f[a[i]+a[j]]) add(i,j,1);
    
    if (dinic()!=n) return puts("Impossible"),0;
    REP(i,1,n) if (a[i]&1) 
        for (int t=head[i]; t; t=e[t].next) 
            if (!e[t].w&&e[t].to<=n) 
                g[i].pb(e[t].to);
                g[e[t].to].pb(i);
            
        
    
    REP(i,1,n) vis[i] = 0;
    int cnt = 0;
    REP(i,1,n) if (!vis[i])  
        ++cnt;
        int j = i;
        while (1) 
            vis[j] = 1;
            ans[cnt].pb(j);
            if (vis[g[j][0]]&&vis[g[j][1]]) break;
            if (vis[g[j][0]]) j=g[j][1];
            else j=g[j][0];
        
    
    printf("%d\\n", cnt);
    REP(i,1,cnt)  
        printf("%d ", (int)ans[i].size());
        for (int j:ans[i]) printf("%d ",j);
        puts("");
    
View Code

513F

大意: $n*m$的矩阵, 每个格子能住两个人, 给定若干个人的位置以及移动速度, 求最短多少时间, 能使所有人都和一个异性分到一个房间.

二分答案, 建图, 看最大流是否满流.

技术图片
#include <iostream>
#include <cstdio>
#include <queue>
#include <string.h>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
const int N = 1e6+10, S = N-2, T = N-1, INF = 0x3f3f3f3f;
struct edge 
    int to,w,next;
    edge(int to=0,int w=0,int next=0):to(to),w(w),next(next)
 e[N];
int head[N], dep[N], vis[N], cur[N], cnt=1;
queue<int> Q;
int bfs(int n) 
    REP(i,1,n) dep[i]=INF,vis[i]=0,cur[i]=head[i];
    dep[S]=INF,vis[S]=0,cur[S]=head[S];
    dep[T]=INF,vis[T]=0,cur[T]=head[T];
    dep[S]=0,Q.push(S);
    while (Q.size()) 
        int u = Q.front(); Q.pop();
        for (int i=head[u]; i; i=e[i].next) 
            if (dep[e[i].to]>dep[u]+1&&e[i].w) 
                dep[e[i].to]=dep[u]+1;
                Q.push(e[i].to);
            
        
    
    return dep[T]!=INF;

int dfs(int x, int w) 
    if (x==T) return w;
    int used = 0;
    for (int i=cur[x]; i; i=e[i].next) 
        cur[x] = i;
        if (dep[e[i].to]==dep[x]+1&&e[i].w) 
            int f = dfs(e[i].to,min(w-used,e[i].w));
            if (f) used+=f,e[i].w-=f,e[i^1].w+=f;
            if (used==w) break;
        
    
    return used;

int dinic(int n) 
    int ans = 0;
    while (bfs(n)) ans+=dfs(S,INF);
    return ans;

void add(int u, int v, int w) 
    e[++cnt] = edge(v,w,head[u]);
    head[u] = cnt;
    e[++cnt] = edge(u,0,head[v]);
    head[v] = cnt;
 

int n,m,x,y;
struct _ int x,y,t; h, a[N], b[N];
char s[33][33];
int v[33][33];
int ID(int x, int y) 
    return (x-1)*m+y;

const int dx[]=0,0,-1,1;
const int dy[]=-1,1,0,0;
int chk(ll tot) 
    cnt = 1;
    REP(i,1,2*x+2*n*m) head[i]=0;
    head[S]=head[T]=0;
    queue<_> q;
    REP(i,1,x)  
        add(S,i,1);
        q.push(a[i].x,a[i].y,0);
        memset(v,0,sizeof v);
        while (q.size()) 
            _ u = q.front(); q.pop();
            if (s[u.x][u.y]==#||v[u.x][u.y]) continue;
            if (a[i].t&&u.t>tot/a[i].t) continue;
            v[u.x][u.y] = 1;
            REP(k,0,3) 
                int xx=u.x+dx[k],yy=u.y+dy[k];
                if (1<=xx&&xx<=n&&1<=yy&&yy<=m) 
                    q.push(xx,yy,u.t+1);
                
            
        
        REP(j,1,n) REP(k,1,m) if (v[j][k]) add(i,ID(j,k)+2*x,1);
    
    REP(i,1,x)  
        add(i+x,T,1);
        q.push(b[i].x,b[i].y,0);
        memset(v,0,sizeof v);
        while (q.size()) 
            _ u = q.front(); q.pop();
            if (s[u.x][u.y]==#||v[u.x][u.y]) continue;
            if (b[i].t&&u.t>tot/b[i].t) continue;
            v[u.x][u.y] = 1;
            REP(k,0,3) 
                int xx=u.x+dx[k],yy=u.y+dy[k];
                if (1<=xx&&xx<=n&&1<=yy&&yy<=m) 
                    q.push(xx,yy,u.t+1);
                
            
        
        REP(j,1,n) REP(k,1,m) if (v[j][k]) add(ID(j,k)+2*x+n*m,i+x,1);
    
    REP(i,1,n) REP(j,1,m) if (s[i][j]!=#) add(ID(i,j)+2*x,ID(i,j)+2*x+n*m,1);
    return dinic(2*x+2*n*m)==x;


int main() 
    scanf("%d%d%d%d", &n, &m, &x, &y);
    if (abs(x-y)!=1) return puts("-1"),0;
    REP(i,1,n) scanf("%s",s[i]+1);
    scanf("%d%d%d",&h.x,&h.y,&h.t);
    REP(i,1,x) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].t);
    REP(i,1,y) scanf("%d%d%d",&b[i].x,&b[i].y,&b[i].t);
    if (x>y) b[++y]=h;
    else a[++x]=h;
    ll l=0,r=1e18,ans=-1;
    while (l<=r) 
        ll mid = (l+r)/2;
        if (chk(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    
    printf("%lld\\n", ans);
View Code

546E

 

611H

 

以上是关于CF网络流练习的主要内容,如果未能解决你的问题,请参考以下文章

CF331EBiologist(网络流,最小割)

CF802CHeidi and Library(网络流)

cwl的网络流24题练习

网络流练习题 比特板

[CF Gym101196-I] Waif Until Dark 网络最大流

网络流之最大流