zoukankan      html  css  js  c++  java
  • 网络流24题

    ①飞行员配对方案【二分匹配】

    洛谷P2756
    二分图匹配 + 输出方案

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    #define cls(x) memset(x,0,sizeof(x))
    using namespace std;
    const int maxn = 305,maxm = 100005,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T,N,M;
    struct EDGE{int to,f,nxt;}ed[maxm];
    inline void build(int u,int v,int w){
        ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;
        ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;
    }
    bool bfs(){
        cls(vis); cls(d);
        queue<int> q;
        d[S] = 0; vis[S] = true; q.push(S); int u,to;
        while (!q.empty()){
            u = q.front(); q.pop();
            Redge(u) if (ed[k].f && !vis[to = ed[k].to]){
                d[to] = d[u] + 1; vis[to] = true; q.push(to);
            }
        }
        return vis[T];
    }
    int dfs(int u,int minf){
        if (u == T || !minf) return minf;
        int flow = 0,f,to;
        if (cur[u] == -2) cur[u] = h[u];
        for (int& k = cur[u]; k != -1; k = ed[k].nxt)
            if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){
                ed[k].f -= f; ed[k ^ 1].f += f;
                minf -= f; flow += f;
                if (!minf) break;
            }
        return flow;
    }
    int maxflow(){
        int flow = 0;
        while (bfs()){
            fill(cur,cur + maxn,-2);
            flow += dfs(S,INF);
        }
        return flow;
    }
    int main(){
        memset(h,-1,sizeof(h));
        M = RD(); N = RD(); S = 0; T = N + M + 1; int a,b;
        while (true){
            a = RD(); b = RD();
            if (a == -1 && b == -1) break;
            build(a,b,1);
        }
        REP(i,M) build(S,i,1);
        REP(i,N) build(M + i,T,1);
        int ans = maxflow();
        if (!ans) printf("No Solution!
    ");
        else {
            printf("%d
    ",ans);
            REP(i,M) Redge(i) if (!(k & 1) && !ed[k].f){
                printf("%d %d
    ",i,ed[k].to); break;
            }
        }
        return 0;
    }
    

    ②太空飞行计划问题【最大权闭合子图】

    经典的最大权闭合子图问题【记得我以前写过这个博客】
    选一个正权物品就要附属上一些负权物品
    我们将原图的边变为INF
    之后由S引向正权的点,流量为正权权值
    引负权的点向T,流量也为权值【相反数,也就是正的】

    这样我们假设我们已经获得了所有正权,我们的损失就是最小割
    对于一个正权物品,我们要么丢弃,要么选择支付其附属品

    最后我们再跑一次dfs,S能到达的物品就是被选中的
    【MMP我还没跑最大流就开始跑残量网络。。。查了半天】

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    #define cls(x) memset(x,0,sizeof(x))
    using namespace std;
    const int maxn = 505,maxm = 100005,INF = 0x3f3f3f3f;
    int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T,N,M,sum = 0;
    struct EDGE{int to,f,nxt;}ed[maxm];
    inline void build(int u,int v,int w){
        ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;
        ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;
    }
    bool bfs(){
        cls(d); queue<int> q;
        d[S] = 1; q.push(S); int u,to;
        while (!q.empty()){
            u = q.front(); q.pop();
            if (u == T) return 1;
            Redge(u) if (ed[k].f && !d[to = ed[k].to]){
                d[to] = d[u] + 1; q.push(to);
            }
        }
        return 0;
    }
    int dfs(int u,int minf){
        if (u == T || !minf) return minf;
        int flow = 0,f,to;
        if (cur[u] == -2) cur[u] = h[u];
        for (int& k = cur[u]; k != -1; k = ed[k].nxt)
            if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){
                ed[k].f -= f; ed[k ^ 1].f += f;
                minf -= f; flow += f;
                if (!minf) break;
            }
        return flow;
    }
    int maxflow(){
        int flow = 0;
        while (bfs()){
            fill(cur,cur + maxn,-2);
            flow += dfs(S,INF);
        }
        return flow;
    }
    void DFS(int u){
        int to; vis[u] = true;
        Redge(u) if (ed[k].f > 0 && !vis[to = ed[k].to]) DFS(to);
    }
    int main(){
        memset(h,-1,sizeof(h));
        int x,flow;
        scanf("%d%d",&M,&N);
        S = 0; T = N + M +1;
        for(int i=1;i<=M;++i) {
            scanf("%d",&x),build(S,i,x),sum+=x;
            char ch=getchar();
            while(ch!='
    '&&ch!='
    '&&ch!=EOF) {
                x=0;
                while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
                if(x) build(i,M+x,INF);
                if(ch!='
    '&&ch!='
    '&&ch!=EOF) ch=getchar();
            }
        }
        for(int i=1;i<=N;++i) scanf("%d",&x),build(i+M,T,x);
        flow = maxflow();
        DFS(S);
        REP(i,M) if (vis[i]) printf("%d ",i); putchar('
    ');
        REP(i,N) if (vis[M + i]) printf("%d ",i); putchar('
    ');
        printf("%d",sum - flow);
        return 0;
    }
    

    ③最小路径覆盖问题

    有两种类型:
    ①在DAG图上,选择多条路径【可以只含一个点】,路径经过的点不能有交集,求最小覆盖所有点的路径数
    ②在DAG图上,选择多条路径【可以只含一个点】,路径经过的点能有交集,求最小覆盖所有点的路径数

    对于问题①,我们先把所有点看做一条路径,然后沿边外扩展,一开始答案是点数,最多能扩展多少答案就减少多少条路径

    对于问题②,允许跨过重复的点,我们求一个传递闭包,实际求最大流时就可以跨过重复的点了
    问题①洛谷P2764
    问题②BZOJ1143
    这里粘问题①代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    #define cls(x) memset(x,0,sizeof(x))
    using namespace std;
    const int maxn = 505,maxm = 100005,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T,N,M;
    int pre[maxn],nxt[maxn];
    struct EDGE{int to,f,nxt;}ed[maxm];
    inline void build(int u,int v,int w){
        ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;
        ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;
    }
    bool bfs(){
        cls(vis); cls(d);
        queue<int> q;
        d[S] = 0; vis[S] = true; q.push(S); int u,to;
        while (!q.empty()){
            u = q.front(); q.pop();
            Redge(u) if (ed[k].f && !vis[to = ed[k].to]){
                d[to] = d[u] + 1; vis[to] = true; q.push(to);
            }
        }
        return vis[T];
    }
    int dfs(int u,int minf){
        if (u == T || !minf) return minf;
        int flow = 0,f,to;
        if (cur[u] == -2) cur[u] = h[u];
        for (int& k = cur[u]; k != -1; k = ed[k].nxt)
            if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){
                ed[k].f -= f; ed[k ^ 1].f += f;
                minf -= f; flow += f;
                if (!minf) break;
            }
        return flow;
    }
    int maxflow(){
        int flow = 0;
        while (bfs()){
            fill(cur,cur + maxn,-2);
            flow += dfs(S,INF);
        }
        return flow;
    }
    int main(){
        memset(h,-1,sizeof(h));
        N = RD(); M = RD(); S = 0; T = 2 * N + 1; int a,b;
        REP(i,N) build(S,i,1),build(i + N,T,1);
        while (M--){
            a = RD(); b = RD();
            build(a,b + N,1);
        }
        int ans = maxflow();
        REP(i,N) Redge(i) if (!(k & 1) && !ed[k].f) nxt[i] = ed[k].to - N,pre[ed[k].to - N] = true;
        REP(i,N) if (!pre[i]){
            int u = i;
            while (u) printf("%d ",u),u = nxt[u];
            printf("
    ");
        }
        printf("%d",N - ans);
        return 0;
    }
    

    ④ 魔术球问题 【最小路径覆盖】

    洛谷P2765
    其实也是最小路径覆盖,但求的是在最小路径覆盖数一定情况下所取最多点数
    我们就依次加入点以及对应边,在残量网络上跑,其实效率挺高

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<cmath>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    #define cls(x) memset(x,0,sizeof(x))
    using namespace std;
    const int maxn = 4005,maxm = 100005,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T,s = 0;
    struct EDGE{int to,f,nxt;}ed[maxm];
    inline void build(int u,int v,int w){
        ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;
        ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;
    }
    bool bfs(){
        cls(vis); cls(d);
        queue<int> q;
        d[S] = 1; vis[S] = true; q.push(S); int u,to;
        while (!q.empty()){
            u = q.front(); q.pop();
            Redge(u) if (ed[k].f && !vis[to = ed[k].to]){
                d[to] = d[u] + 1; vis[to] = true; q.push(to);
            }
        }
        return vis[T];
    }
    int dfs(int u,int minf){
        if (u == T || !minf) return minf;
        int flow = 0,f,to;
        if (cur[u] == -2) cur[u] = h[u];
        for (int& k = cur[u]; k != -1; k = ed[k].nxt)
            if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){
                ed[k].f -= f; ed[k ^ 1].f += f;
                minf -= f; flow += f;
                if (!minf) break;
            }
        return flow;
    }
    int maxflow(){
        int flow = 0;
        while (bfs()){
            fill(cur,cur + maxn,-2);
            flow += dfs(S,INF);
        }
        return flow;
    }
    void init(){
        build(S,2 * s - 1,1); build (2 * s,T,1);
        for (int i = 1; i < s; i++){
            int v = (int)sqrt(i + s);
            if (v * v == i + s) build(2 * i - 1,2 * s,1);
        }
    }
    int pre[maxn],nxt[maxn];
    void getans(){
        for (int i = 1; i < s; i++){
            int u = 2 * i - 1;
            Redge(u) if (!(k & 1) && !ed[k].f){
                nxt[i] = ed[k].to >> 1;
                pre[ed[k].to >> 1] = true;
                break;
            }
        }
        for (int i = 1; i < s; i++){
            if (pre[i]) continue;
            int u = i;
            while (u) printf("%d ",u),u = nxt[u];
            printf("
    ");
        }
    }
    int main(){
        memset(h,-1,sizeof(h));
        int n = RD(),flow = 0; S = 0; T = 3501;
        while (true){
            s++; init();
            flow += maxflow();
            if (s - flow > n) break;
        }
        printf("%d
    ",s - 1);
        getans();
        return 0;
    }
    

    今天先到这

    ⑤圆桌问题

    题意:M团人坐N个大小不同的桌,同一团的人不能坐同一桌,求是否有解及一个方案

    QAQ找不到OJ提交,就先放着代码吧
    建图:
    ①S->每个团,容量为人数
    ②圆桌->T,容量为桌子容量
    ③每个团->每个圆桌,容量为1
    跑一遍最大流,看看S的出边是否全都满载
    对于每个团,流向的圆桌就是方案

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    #define cls(x) memset(x,0,sizeof(x))
    using namespace std;
    const int maxn = 305,maxm = 100005,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T,N,M;
    struct EDGE{int to,f,nxt;}ed[maxm];
    inline void build(int u,int v,int w){
        ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;
        ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;
    }
    bool bfs(){
        cls(vis); cls(d);
        queue<int> q;
        d[S] = 0; vis[S] = true; q.push(S); int u,to;
        while (!q.empty()){
            u = q.front(); q.pop();
            Redge(u) if (ed[k].f && !vis[to = ed[k].to]){
                d[to] = d[u] + 1; vis[to] = true; q.push(to);
            }
        }
        return vis[T];
    }
    int dfs(int u,int minf){
        if (u == T || !minf) return minf;
        int flow = 0,f,to;
        if (cur[u] == -2) cur[u] = h[u];
        for (int& k = cur[u]; k != -1; k = ed[k].nxt)
            if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){
                ed[k].f -= f; ed[k ^ 1].f += f;
                minf -= f; flow += f;
                if (!minf) break;
            }
        return flow;
    }
    int maxflow(){
        int flow = 0;
        while (bfs()){
            fill(cur,cur + maxn,-2);
            flow += dfs(S,INF);
        }
        return flow;
    }
    int main(){
        memset(h,-1,sizeof(h));
        int M = RD(),N = RD(),tot = 0,x; S = 0; T = N + M + 1;
        REP(i,M) build(S,i,x = RD()),tot += x;
        REP(i,N) build(i + M,T,RD());
        REP(i,M) REP(j,N) build(i,M + j,1);
        int ans = maxflow();
        if (ans != tot) {printf("0"); return 0;}
        printf("1
    ");
        REP(i,M){
            Redge(i) if (!(k & 1) && !ed[k].f) printf("%d ",ed[k].to - M);
            putchar('
    ');
        }
        return 0;
    }
    

    ⑥最长递增子序列问题

    洛谷2766
    求出序列中每个数最多选一次,最多能组成多少个最长递增子序列

    我们先DP算出最长长度K,然后建图:
    S->f为1的点,容量1
    f为K的点->T,容量1
    所有f[i] == f[j] - 1,i->j,容量1
    跑一遍最大流即可

    如果首尾能使用多次,将其对应和源汇点的边改为INF即可

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    using namespace std;
    const int maxn=1005,INF=2000000000;
    
    inline int read(){
        int out=0,flag=1;char c=getchar();
        while(c<48||c>57) {if(c=='-') flag=-1;c=getchar();}
        while(c>=48&&c<=57) {out=out*10+c-48;c=getchar();}
        return out*flag;
    }
    
    int A[maxn],dp[maxn],N,K=0;
    
    int head[maxn],nedge=0;
    struct EDGE{
        int to,f,c,next;
    }edge[maxn*maxn];
    
    inline void build(int a,int b,int w){
        edge[nedge]=(EDGE){b,0,w,head[a]};
        head[a]=nedge++;
        edge[nedge]=(EDGE){a,0,0,head[b]};
        head[b]=nedge++;
    }
    
    bool vis[maxn];
    int d[maxn],S,T,cur[maxn],lans;
    
    bool bfs(){
        fill(vis,vis+maxn,false);
        queue<int> q;
        q.push(S);
        vis[S]=true;
        d[S]=0;
        int u,to;
        while(!q.empty()){
            u=q.front();
            q.pop();
            for(int k=head[u];k!=-1;k=edge[k].next)
                if(!vis[to=edge[k].to]&&edge[k].f<edge[k].c){
                    d[to]=d[u]+1;
                    vis[to]=true;
                    q.push(to);
                }
        }
        return vis[T];
    }
    
    int dfs(int u,int minf){
        if(u==T||!minf) return minf;
        int f,flow=0,to;
        if(cur[u]==-2) cur[u]=head[u];
        for(int& k=cur[u];k!=-1;k=edge[k].next)
            if(d[to=edge[k].to]==d[u]+1&&(f=dfs(to,min(minf,edge[k].c-edge[k].f)))){
                edge[k].f+=f;
                edge[k^1].f-=f;
                flow+=f;
                minf-=f;
                if(!minf) break;
            }
        return flow;
    }
    
    int maxflow(){
        int flow=0;
        while(bfs()){
            fill(cur,cur+maxn,-2);
            flow+=dfs(S,INF);
        }
        return flow;
    }
    
    void solve1(){
        S=0;
        T=2*N+1;
        for(int i=1;i<=N;i++){
            build(i,i+N,1);
            if(dp[i]==1) build(S,i,1);
            if(dp[i]==K) build(i+N,T,1);
            for(int j=1;j<i;j++)
                if(A[j]<=A[i]&&dp[j]==dp[i]-1)
                    build(j+N,i,1);
        }
        lans=maxflow();
        cout<<lans<<endl;
    }
    
    void solve2(){
        for(int k=head[S];k!=-1;k=edge[k].next)
            if(edge[k].to==1){edge[k].c=INF;break;}
        for(int k=head[1];k!=-1;k=edge[k].next)
            if(edge[k].to==N+1){edge[k].c=INF;break;}
        for(int k=head[N];k!=-1;k=edge[k].next)
            if(edge[k].to==N+N){edge[k].c=INF;break;}
        for(int k=head[N+N];k!=-1;k=edge[k].next)
            if(edge[k].to==T){edge[k].c=INF;break;}
        cout<<lans+maxflow()<<endl;
    }
    
    int main()
    {
        fill(head,head+maxn,-1);
        N=read();
        for(int i=1;i<=N;i++) A[i]=read();
        for(int i=1;i<=N;i++){
            dp[i]=1;
            for(int j=1;j<i;j++)
                if(A[j]<=A[i]) dp[i]=max(dp[i],dp[j]+1);
        }
        for(int i=1;i<=N;i++) if(dp[i]>K) K=dp[i];
        cout<<K<<endl;
        solve1();
        solve2();
        return 0;
    }
    

    ⑦试题库问题

    洛谷P2763
    每种类型选择一定数量,每种类型都有对应的一些题可以选择
    实际就是多匹配的问题
    建图:
    ①S->类型,容量为对应需要的数量
    ②题目->T,容量为1【每个题只能用一次】
    ③类型->对应题目,容量1

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    #define cls(x) memset(x,0,sizeof(x))
    using namespace std;
    const int maxn = 1305,maxm = 1000005,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T;
    struct EDGE{int to,f,nxt;}ed[maxm];
    inline void build(int u,int v,int w){
        ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;
        ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;
    }
    bool bfs(){
        cls(vis); cls(d);
        queue<int> q;
        d[S] = 0; vis[S] = true; q.push(S); int u,to;
        while (!q.empty()){
            u = q.front(); q.pop();
            Redge(u) if (ed[k].f && !vis[to = ed[k].to]){
                d[to] = d[u] + 1; vis[to] = true; q.push(to);
            }
        }
        return vis[T];
    }
    int dfs(int u,int minf){
        if (u == T || !minf) return minf;
        int flow = 0,f,to;
        if (cur[u] == -2) cur[u] = h[u];
        for (int& k = cur[u]; k != -1; k = ed[k].nxt)
            if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){
                ed[k].f -= f; ed[k ^ 1].f += f;
                minf -= f; flow += f;
                if (!minf) break;
            }
        return flow;
    }
    int maxflow(){
        int flow = 0;
        while (bfs()){
            fill(cur,cur + maxn,-2);
            flow += dfs(S,INF);
        }
        return flow;
    }
    int main(){
        memset(h,-1,sizeof(h));
        int K = RD(),N = RD(),M = 0,x,p; S = 0; T = K + N + 1;
        REP(i,K) build(S,i,x = RD()),M += x;
        REP(i,N){
            p = RD(); build(K + i,T,1);
            while (p--) build(RD(),K + i,1);
        }
        int ans = maxflow();
        if (ans != M) {printf("No Solution!"); return 0;}
        REP(i,K){
            printf("%d:",i);
            Redge(i) if (!(k & 1) && !ed[k].f){
                printf(" %d",ed[k].to - K);
            }
            putchar('
    ');
        }
        return 0;
    }
    

    ⑧机器人路径规划问题

    听说很难

    ⑨方格取数问题【最大点独立集】

    BZOJ1475 洛谷2774
    方格取数,所取数不能相邻
    我们对矩阵黑白染色就会发现同种颜色互不干涉,相邻的黑白只能取一
    这就是最大点独立集呐

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    #define cls(x) memset(x,0,sizeof(x))
    using namespace std;
    const int maxn = 1005,maxm = 200005,INF = 0x7fffffff;
    inline LL RD(){
        LL out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T;
    struct EDGE{int to,nxt; LL f;}ed[maxm];
    inline void build(int u,int v,LL w){
        ed[ne] = (EDGE){v,h[u],w}; h[u] = ne++;
        ed[ne] = (EDGE){u,h[v],0}; h[v] = ne++;
    }
    bool bfs(){
        cls(vis); cls(d);
        queue<int> q;
        d[S] = 0; vis[S] = true; q.push(S); int u,to;
        while (!q.empty()){
            u = q.front(); q.pop();
            Redge(u) if (ed[k].f && !vis[to = ed[k].to]){
                d[to] = d[u] + 1; vis[to] = true; q.push(to);
            }
        }
        return vis[T];
    }
    LL dfs(int u,LL minf){
        if (u == T || !minf) return minf;
        LL flow = 0,f,to;
        if (cur[u] == -2) cur[u] = h[u];
        for (int& k = cur[u]; k != -1; k = ed[k].nxt)
            if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){
                ed[k].f -= f; ed[k ^ 1].f += f;
                minf -= f; flow += f;
                if (!minf) break;
            }
        return flow;
    }
    LL maxflow(){
        LL flow = 0;
        while (bfs()){
            fill(cur,cur + maxn,-2);
            flow += dfs(S,INF);
        }
        return flow;
    }
    int main(){
        memset(h,-1,sizeof(h));
        int n = RD(),N = n * n; S = 0; T = N + 1; LL x,sum = 0;
        REP(i,n) REP(j,n){
            int u = n * (i - 1) + j;
            if ((i & 1) ^ (j & 1)){
                build(S,u,x = RD());
                if (i > 1) build(u,u - n,INF);
                if (i < n) build(u,u + n,INF);
                if (j > 1) build(u,u - 1,INF);
                if (j < n) build(u,u + 1,INF);
            }
            else build(u,T,x = RD());
            sum += x;
        }
        cout<<sum - maxflow()<<endl;
        return 0;
    }
    

    ⑩餐巾计划问题【线性规划与网络流】

    洛谷P1251
    问题见原题。【大概就是每天要用一定量的餐巾,要么直接购买,要么通过之前用的送到快洗慢洗店洗得,求最小费用】
    想清楚的话建图还是很容易想到的:
    拆点,一分为三,记为xi,yi,zi,分别表示购买餐巾、使用餐巾、送洗餐巾
    建边:
    ①S->xi,容量INF,费用0【商店的餐巾】
    ②yi->T,容量Ri,费用0【使用的餐巾】
    ③xi->yi,容量INF,费用pi【由商店到餐厅,需要消费】
    ④xi->zi,容量Ri,费用0【每天会产生Ri个使用过的餐巾】
    ⑤zi->xj,有两条,容量INF,费用f或s,分别指向i + m和i + n【如果送洗,完成后立刻拿回来】
    ⑥yi->y(i+1),容量INF,费用0【放着不用的纸巾】

    跑一遍费用流,就可以得到答案【很形象的其实,有没有】

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    using namespace std;
    const int maxn = 6005,maxm = 100005;
    LL INF = 10000000000000000ll;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int h[maxn],ne = 0,S,T,pa[maxn],minf[maxn],R[maxn];
    LL d[maxn];
    bool inq[maxn];
    struct EDGE{int from,to,nxt,f; LL w;}ed[maxm];
    inline void build(int u,int v,int f,LL w){
        ed[ne] = (EDGE){u,v,h[u],f,w};  h[u] = ne++;
        ed[ne] = (EDGE){v,u,h[v],0,-w};  h[v] = ne++;
    }
    LL mincost(){
        queue<int> q; LL u,to,flow = 0,cost = 0;
        while (true){
            for (int i = 0; i <= T; i++) d[i] = INF,inq[i] = false,minf[i] = INF;
            d[S] = 0; q.push(S);
            while (!q.empty()){
                u = q.front(); q.pop();
                inq[u] = false;
                Redge(u) if (ed[k].f && d[to = ed[k].to] > d[u] + ed[k].w){
                    d[to] = d[u] + ed[k].w; pa[to] = k; minf[to] = min(minf[u],ed[k].f);
                    if (!inq[to]) q.push(to),inq[to] = true;
                }
            }
            if (d[T] == INF) break;
            flow += minf[T]; cost += minf[T] * d[T];
            u = T;
            while (u){
                ed[pa[u]].f -= minf[T]; ed[pa[u]^1].f += minf[T];
                u = ed[pa[u]].from;
            }
        }
        return cost;
    }
    int main(){
        memset(h,-1,sizeof(h));
        int N = RD(); S = 0; T = 3 * N + 1;
        REP(i,N) build(S,i,INF,0),build(i + N,T,R[i] = RD(),0);
        int p = RD(),m = RD(),f = RD(),n = RD(),s = RD();
        REP(i,N){
            build(i,i + N,INF,p);
            build(i,i + 2 * N,R[i],0);
            if (i + m <= N) build(i + 2 * N,i + m + N,INF,f);
            if (i + n <= N) build(i + 2 * N,i + n + N,INF,s);
            if (i < N) build(i + N,i + 1 + N,INF,0);
        }
        printf("%lld
    ",mincost());
        return 0;
    }
    

    ⑪航空路线问题 【最大费用最大流】

    洛谷P2770
    寻找DAG图中从一个点出发到终点的两条不相交路径,使得经过尽量多的点

    我们拆点使得每个点流量1费用为1,起点终点流量为2,其他边流量1费用0,跑一遍最大费用最大流【就是每次找DAG最长路】

    【坑点,ST直接相连,流量要为2】

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<map>
    #include<string>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    using namespace std;
    const int maxn = 305,maxm = 100005,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    map<string,int> H;
    string name[maxn];
    int h[maxn],ne = 0,S,T,p[maxn],minf[maxn],d[maxn],id = 0;
    bool inq[maxn],vis[maxn];
    struct EDGE{int from,to,f,w,nxt;}ed[maxm];
    inline void build(int u,int v,int f,int w){
        ed[ne] = (EDGE){u,v,f,w,h[u]};  h[u] = ne++;
        ed[ne] = (EDGE){v,u,0,-w,h[v]};  h[v] = ne++;
    }
    int maxcost(){
        queue<int> q; int u,to,flow = 0,cost = 0;
        while (true){
            for (int i = S; i <= T; i++) d[i] = -1,inq[i] = false,minf[i] = INF;
            d[S] = 0; q.push(S);
            while (!q.empty()){
                u = q.front(); q.pop();
                inq[u] = false;
                Redge(u) if (ed[k].f && d[to = ed[k].to] < d[u] + ed[k].w){
                    d[to] = d[u] + ed[k].w; p[to] = k; minf[to] = min(minf[u],ed[k].f);
                    if (!inq[to]) q.push(to),inq[to] = true;
                }
            }
            if (d[T] == -1) break;
            flow += minf[T]; cost += minf[T] * d[T];
            u = T;
            while (u != S){
                ed[p[u]].f -= minf[T]; ed[p[u]^1].f += minf[T];
                u = ed[p[u]].from;
            }
        }
        return cost;
    }
    int code(const string& s){
        if (!H.count(s)) H[s] = ++id,name[id] = s;
        return H[s];
    }
    int main(){
        memset(h,-1,sizeof(h));
        int N = RD(),V = RD(),a,b; S = 1; T = 2 * N;
        string s;
        REP(i,N) cin>>s,code(s);
        build(1,N + 1,2,1); build(N,N + N,2,1);
        for (int i = 2; i < N; i++) build(i,i + N,1,1);
        while (V--){
            cin>>s; a = code(s);
            cin>>s; b = code(s);
            if (a > b) swap(a,b);
            if (a == 1 && b == N) build(a + N,b,2,0);
            else build(a + N,b,1,0);
        }
        int ans = maxcost() - 2;
        if (ed[h[S]].f) {printf("No Solution!
    "); return 0;}
        printf("%d
    ",ans);
        int u = 1 + N,to;
        cout<<name[1]<<endl;
        while (u != N + N){
            Redge(u) if (!(k & 1) && !ed[k].f && !vis[to = ed[k].to]){
                cout<<name[to]<<endl; vis[to] = true;
                u = to + N; break;
            }
        }
        u = N;
        while (u != S){
            Redge(u) if ((k & 1) && !ed[k ^ 1].f && !vis[to = (ed[k].to - N)]){
                cout<<name[to]<<endl; vis[to] = true;
                u = to; break;
            }
        }
    
        return 0;
    }
    

    ⑫软件补丁问题【最小代价转移】

    洛谷P2761
    这。。。网络流?
    状压最短路可以写,1表示有该BUG,0表示没有改BUG,跑一遍SPFA
    对于每个点,检查每一个补丁,看看是否满足B1含有而B2 不含有
    然后去掉F1(u^F&u)并上F2
    用到一些位运算技巧

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    using namespace std;
    const int maxn = 105,maxm = 1 << 20,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int w[maxn],B1[maxn],B2[maxn],F1[maxn],F2[maxn],n,m,d[maxm];
    bool inq[maxm];
    queue<int> q;
    void SPFA(){
        fill(d,d + maxm,INF);
        q.push((1 << n) - 1); inq[(1 << n) - 1] = true; d[(1 << n) - 1] = 0;
        int u,to;
        while (!q.empty()){
            u = q.front(); q.pop();
            inq[u] = false;
            REP(i,m) if ((u & B1[i]) == B1[i] && !(u & B2[i])){
                to = (u ^ F1[i] & u) | F2[i];
                if (d[u] + w[i] < d[to]){
                    d[to] = d[u] + w[i];
                    if (!inq[to]) q.push(to),inq[to] = true;
                }
            }
        }
        if (d[0] == INF) printf("0");
        else printf("%d",d[0]);
    }
    int main(){
        n = RD(); m = RD();
        REP(i,m){
            w[i] = RD();
            REP(j,n){
                char c = getchar(); B1[i] <<= 1; B2[i] <<= 1;
                while (c != '0' && c != '-' && c != '+') c = getchar();
                if (c == '+') B1[i] |= 1;
                if (c == '-') B2[i] |= 1;
            }
            REP(j,n){
                char c = getchar(); F1[i] <<= 1; F2[i] <<= 1;
                while (c != '0' && c != '-' && c != '+') c = getchar();
                if (c == '-') F1[i] |= 1;
                if (c == '+') F2[i] |= 1;
            }
        }
        SPFA();
        return 0;
    }
    

    ⑬星际转移问题

    洛谷P2754
    很巧妙的建图
    我们先用并查集判断是否有解
    再分点,每个空间站分成第1天、第2天、第3天……
    【E表示地球,M表示月亮】
    ①S->E,容量K,有K个人要走
    ②空间站每天向下一天建一条容量INF的边,表示逗留
    ③根据飞穿走势,每天的空间站向下一天的到达的空间站建容量为飞船容量的边
    ④M->T,容量INF,到达月球
    我们并不需要把每一天都建出来
    枚举天数,不断建图即可
    【竟然1A了。。】

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    #define cls(x) memset(x,0,sizeof(x))
    using namespace std;
    const int maxn = 20005,maxm = 100005,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T,E,M,D;
    struct EDGE{int to,f,nxt;}ed[maxm];
    inline void build(int u,int v,int w){
        ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;
        ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;
    }
    bool bfs(){
        cls(vis); cls(d);
        queue<int> q;
        d[S] = 0; vis[S] = true; q.push(S); int u,to;
        while (!q.empty()){
            u = q.front(); q.pop();
            Redge(u) if (ed[k].f && !vis[to = ed[k].to]){
                d[to] = d[u] + 1; vis[to] = true; q.push(to);
            }
        }
        return vis[T];
    }
    int dfs(int u,int minf){
        if (u == T || !minf) return minf;
        int flow = 0,f,to;
        if (cur[u] == -2) cur[u] = h[u];
        for (int& k = cur[u]; k != -1; k = ed[k].nxt)
            if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){
                ed[k].f -= f; ed[k ^ 1].f += f;
                minf -= f; flow += f;
                if (!minf) break;
            }
        return flow;
    }
    int maxflow(){
        int flow = 0;
        while (bfs()){
            fill(cur,cur + maxn,-2);
            flow += dfs(S,INF);
        }
        return flow;
    }
    int pre[maxn],w[maxn],way[25][25],pos[25],t[25],La[25];
    int find(int u) {return u == pre[u] ? u : pre[u] = find(pre[u]);}
    int main(){
        memset(h,-1,sizeof(h));
        int n = RD(),m = RD(),K = RD(); S = 0; T = 10000; E = 9998; M = 9999;
        REP(i,n) pre[i] = i; pre[E] = E; pre[M] = M;
        REP(i,m){
            w[i] = RD(); t[i] = RD(); int last = 0;
            REP(j,t[i]){
                way[i][j] = RD();
                if (way[i][j] == 0) way[i][j] = E;
                if (way[i][j] == -1) way[i][j] = M;
                if (last){
                    int fa = find(last),fb = find(way[i][j]);
                    if (fa != fb) pre[fb] = fa;
                }
                last = way[i][j];
            }
        }
        if (find(E) != find(M)) {printf("0"); return 0;}
        build(S,E,K); build(M,T,INF);
        D = 0; int flow = 0;
        while (true){
            ++D;
            if (D > 1) REP(i,n) build(i + m * (D - 2),i + m * (D - 1),INF);
            REP(i,m){
                pos[i]++; if (pos[i] > t[i]) pos[i] = 1;
                int to = way[i][pos[i]],u,v;
                if (La[i]){
                    u = La[i]; if (u != E && u != M) u += m * (D - 2);
                    v = to; if (v != E && v != M) v += m * (D - 1);
                    build(u,v,w[i]);
                }
                La[i] = to;
            }
            flow += maxflow();
            if (flow == K) break;
        }
        printf("%d",D - 1);
        return 0;
    }
    

    ⑭孤岛营救问题

    洛谷P4011
    建立分层图,拿着不同的钥匙组合对应不同层的图,每层图的边是在拿了对应钥匙基础上建立的,然后就是最短路了
    实际并不需要把边建出来,写起来有点像搜索
    【一开始把标号算错,拿N去乘横坐标。。我蠢好吧。】

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    using namespace std;
    const int maxn = 105,maxm = 1 << 15,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int N,M,S,P,K,G[maxn][maxn],X[4] = {0,0,-1,1},Y[4] = {1,-1,0,0};
    int Key[15][15],ans = INF,d[maxm][15][15];
    bool inq[maxm][15][15];
    struct node{int s,x,y;};
    queue<node> q;
    void SPFA(){
        fill(d[0][0],d[0][0] + 15 * 15 * maxm,INF);
        q.push((node){0,1,1}); inq[0][1][1] = true; d[0][1][1] = 0;
        int x,y,e,a,b; node u;
        while (!q.empty()){
            u = q.front(); q.pop();
            a = M * (u.x - 1) + u.y;
            inq[u.s][u.x][u.y] = false;
            if (u.x == N && u.y == M) ans = min(ans,d[u.s][u.x][u.y]);
            for (int i = 0; i < 4; i++){
                x = u.x + X[i]; y = u.y + Y[i]; e = u.s; b = M * (x - 1) + y;
                if (x < 1 || y < 1 || x > N || y > M || G[a][b] == INF) continue;
                if ((u.s | G[a][b]) != u.s) continue;
                e |= Key[x][y];
                if (d[e][x][y] > d[u.s][u.x][u.y] + 1){
                    d[e][x][y] = d[u.s][u.x][u.y] + 1;
                    if (!inq[e][x][y]) q.push((node){e,x,y}),inq[e][x][y] = true;
                }
            }
        }
    }
    int main(){
        N = RD(); M = RD(); P = RD(); K = RD(); int x1,x2,y1,y2,w,a,b;
        REP(i,K){
            x1 = RD(); y1 = RD(); x2 = RD(); y2 = RD(); w = RD();
            a = M * (x1 - 1) + y1; b = M * (x2 - 1) + y2;
            if (!w) G[a][b] = G[b][a] = INF;
            else if (G[a][b] != INF) G[a][b] |= (1 << w - 1),G[b][a] |= (1 << w - 1);
        }
        S = RD();
        REP(i,S) x1 = RD(),y1 = RD(),w = RD(),Key[x1][y1] |= (1 << w - 1);
        SPFA();
        if (ans == INF) printf("-1");
        else printf("%d",ans);
        return 0;
    }
    

    ⑮汽车加油行驶问题

    洛谷P4009
    以汽车所含的汽油为状态,建K层的分层图
    每一层【没油除外】向下一层相邻点移动,默认权值0【表示消耗一点油走一步】
    如果走到加油站,边权加上A,并且只能指向顶层【满油】的点加油站只存在满油的点
    如果往上或往左走,边权加上B
    没有加油站的点0层向K层建边,费用C + A,表示建加油站

    跑一次SPFA即可

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    using namespace std;
    const int maxn = 105,maxm = 5000005,maxv = maxn * maxn * 11,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int N,K,A,B,C,T,sq[maxn][maxn],X[4] = {0,0,-1,1},Y[4] = {1,-1,0,0};
    int h[maxv],ne = 0,d[maxv];
    bool inq[maxv];
    struct EDGE{int to,nxt,w;}ed[maxm];
    inline void build(int u,int v,int w){ed[ne] = (EDGE){v,h[u],w}; h[u] = ne++;}
    queue<int> q;
    void SPFA(){
        fill(d,d + maxv,INF); int S = T * K + 1;
        q.push(S); inq[S] = true; d[S] = 0;
        int u,to;
        while (!q.empty()){
            u = q.front(); q.pop();
            inq[u] = false;
            Redge(u) if (d[to = ed[k].to] > d[u] + ed[k].w){
                d[to] = d[u] + ed[k].w;
                if (!inq[to]) q.push(to),inq[to] = true;
            }
        }
    }
    int main(){
        memset(h,-1,sizeof(h));
        N = RD(); K = RD(); A = RD(); B = RD(); C = RD(); T = N * N;
        int nx,ny,v,cost;
        REP(i,N) REP(j,N) sq[i][j] = RD();
        REP(i,N) REP(j,N){
            int u = N * (i - 1) + j;
            if (!sq[i][j]){
                build(u,T * K + u,A + C);
                for (int k = 0; k < 4; k++){
                    nx = i + X[k]; ny = j + Y[k]; cost = 0;
                    if (nx < 1 || ny < 1 || nx > N || ny > N) continue;
                    if (nx < i || ny < j) cost = B;
                    v = N * (nx - 1) + ny;
                    if (!sq[nx][ny]) REP(p,K) build(T * p + u,T * (p - 1) + v,cost);
                    else REP(p,K) build(T * p + u,T * K + v,A + cost);
                }
            }else {
                for (int k = 0; k < 4; k++){
                    nx = i + X[k]; ny = j + Y[k]; cost = 0;
                    if (nx < 1 || ny < 1 || nx > N || ny > N) continue;
                    if (nx < i || ny < j) cost = B;
                    v = N * (nx - 1) + ny;
                    if (!sq[nx][ny]) build(T * K + u,T * (K - 1) + v,cost);
                    else build(T * K + u,T * K + v,A + cost);
                }
            }
        }
        SPFA();
        int ans = INF;
        for (int p = 0; p <= K; p++) ans = min(ans,d[T * (p + 1)]);
        printf("%d",ans);
        return 0;
    }
    

    ⑯数字梯形问题【最大费用最大流】

    洛谷P4013
    有三个问,难度递减:
    一、路径可相交
    ①S向第一层连边,容量1,费用为该点点权
    ②每一层向下一层两个连边,容量INF,费用为下一层点点权
    ③最后一层向T连边,容量INF,费用0
    二、只有点可以相交,路径不重合
    把向下一层连的边容量改为1
    三、全部不可相交
    拆点,入点连出点,容量1,费用0

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    using namespace std;
    const int maxn = 1005,maxm = 100005,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int M,N,h[maxn],ne = 0,S,T,p[maxn],minf[maxn],d[maxn];
    int sq[25][25],id[25][25],cnt = 0;
    bool inq[maxn];
    struct EDGE{int from,to,f,w,nxt;}ed[maxm];
    inline void build(int u,int v,int f,int w){
        ed[ne] = (EDGE){u,v,f,w,h[u]};  h[u] = ne++;
        ed[ne] = (EDGE){v,u,0,-w,h[v]};  h[v] = ne++;
    }
    int maxcost(){
        queue<int> q; int u,to,flow = 0,cost = 0;
        while (true){
            for (int i = 0; i <= T; i++) d[i] = -1,inq[i] = false,minf[i] = INF;
            d[S] = 0; q.push(S);
            while (!q.empty()){
                u = q.front(); q.pop();
                inq[u] = false;
                Redge(u) if (ed[k].f && d[to = ed[k].to] < d[u] + ed[k].w){
                    d[to] = d[u] + ed[k].w; p[to] = k; minf[to] = min(minf[u],ed[k].f);
                    if (!inq[to]) q.push(to),inq[to] = true;
                }
            }
            if (d[T] == -1) break;
            flow += minf[T]; cost += minf[T] * d[T];
            u = T;
            while (u){
                ed[p[u]].f -= minf[T]; ed[p[u]^1].f += minf[T];
                u = ed[p[u]].from;
            }
        }
        return cost;
    }
    void solve1(){
        ne = 0; memset(h,-1,sizeof(h));
        REP(i,N) REP(j,M + i - 1) build(id[i][j],id[i][j] + cnt,1,0);
        for (int i = 1; i <= M; i++) build(S,id[1][i],1,sq[1][i]);
        for (int i = 1; i < N; i++){
            for (int j = 1; j <= M + i - 1; j++){
                build(id[i][j] + cnt,id[i + 1][j],1,sq[i + 1][j]);
                build(id[i][j] + cnt,id[i + 1][j + 1],1,sq[i + 1][j + 1]);
            }
        }
        for (int i = 1; i < N + M; i++) build(id[N][i] + cnt,T,1,0);
        printf("%d
    ",maxcost());
    }
    void  solve2(){
        ne = 0; memset(h,-1,sizeof(h));
        for (int i = 1; i <= M; i++) build(S,id[1][i],1,sq[1][i]);
        for (int i = 1; i < N; i++){
            for (int j = 1; j <= M + i - 1; j++){
                build(id[i][j],id[i + 1][j],1,sq[i + 1][j]);
                build(id[i][j],id[i + 1][j + 1],1,sq[i + 1][j + 1]);
            }
        }
        for (int i = 1; i < N + M; i++) build(id[N][i],T,INF,0);
        printf("%d
    ",maxcost());
    }
    void solve3(){
        ne = 0; memset(h,-1,sizeof(h));
        for (int i = 1; i <= M; i++) build(S,id[1][i],1,sq[1][i]);
        for (int i = 1; i < N; i++){
            for (int j = 1; j <= M + i - 1; j++){
                build(id[i][j],id[i + 1][j],INF,sq[i + 1][j]);
                build(id[i][j],id[i + 1][j + 1],INF,sq[i + 1][j + 1]);
            }
        }
        for (int i = 1; i < N + M; i++) build(id[N][i],T,INF,0);
        printf("%d
    ",maxcost());
    }
    int main(){
        memset(h,-1,sizeof(h));
        M = RD(); N = RD(); S = 0; T = 1000;
        REP(i,N) REP(j,M + i - 1) sq[i][j] = RD(),id[i][j] = ++cnt;
        solve1();
        solve2();
        solve3();
        return 0;
    }
    

    ⑰运输问题 【费用流】

    洛谷P4015
    费用流【最大&最小】
    【不知道为什么求最长路就WA,把权值取反跑最短路就A了。。。应该是本身反图就有负权,我初始化-1不太行,以后求最大费用流还是取反跑最小费用流吧。。】

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    using namespace std;
    const int maxn = 505,maxm = 100005,INF = 100000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int h[maxn],ne = 0,S,T,p[maxn],minf[maxn],d[maxn];
    bool inq[maxn];
    struct EDGE{int from,to,f,w,nxt;}ed[maxm];
    inline void build(int u,int v,int f,int w){
        ed[ne] = (EDGE){u,v,f,w,h[u]};  h[u] = ne++;
        ed[ne] = (EDGE){v,u,0,-w,h[v]};  h[v] = ne++;
    }
    LL mincost(){
        queue<int> q; int u,to,flow = 0; LL cost = 0;
        while (true){
            for (int i = 0; i <= T; i++) d[i] = INF,inq[i] = false,minf[i] = INF;
            d[S] = 0; q.push(S);
            while (!q.empty()){
                u = q.front(); q.pop();
                inq[u] = false;
                Redge(u) if (ed[k].f && d[to = ed[k].to] > d[u] + ed[k].w){
                    d[to] = d[u] + ed[k].w; p[to] = k; minf[to] = min(minf[u],ed[k].f);
                    if (!inq[to]) q.push(to),inq[to] = true;
                }
            }
            if (d[T] == INF) break;
            flow += minf[T]; cost += minf[T] * d[T];
            u = T;
            while (u){
                ed[p[u]].f -= minf[T]; ed[p[u]^1].f += minf[T];
                u = ed[p[u]].from;
            }
        }
        return cost;
    }
    int main(){
        memset(h,-1,sizeof(h));
        int M = RD(),N = RD(); S = 0; T = M + N + 1;
        REP(i,M) build(S,i,RD(),0);
        REP(i,N) build(i + M,T,RD(),0);
        REP(i,M) REP(j,N) build(i,j + M,INF,RD());
        printf("%lld
    ",mincost());
        for (int i = 0; i < ne; i += 2){
            ed[i].f += ed[i ^ 1].f,ed[i ^ 1].f = 0;
            ed[i].w = -ed[i].w; ed[i ^ 1].w = -ed[i ^ 1].w;
        }
        printf("%lld
    ",-mincost());
        return 0;
    }
    

    ⑱分配问题

    洛谷P4014
    费用流即可

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    using namespace std;
    const int maxn = 505,maxm = 100005,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int h[maxn],ne = 0,S,T,p[maxn],minf[maxn],d[maxn];
    bool inq[maxn];
    struct EDGE{int from,to,f,w,nxt;}ed[maxm];
    inline void build(int u,int v,int f,int w){
        ed[ne] = (EDGE){u,v,f,w,h[u]};  h[u] = ne++;
        ed[ne] = (EDGE){v,u,0,-w,h[v]};  h[v] = ne++;
    }
    int mincost(){
        queue<int> q; int u,to,flow = 0,cost = 0;
        while (true){
            for (int i = 0; i <= T; i++) d[i] = INF,inq[i] = false,minf[i] = INF;
            d[S] = 0; q.push(S);
            while (!q.empty()){
                u = q.front(); q.pop();
                inq[u] = false;
                Redge(u) if (ed[k].f && d[to = ed[k].to] > d[u] + ed[k].w){
                    d[to] = d[u] + ed[k].w; p[to] = k; minf[to] = min(minf[u],ed[k].f);
                    if (!inq[to]) q.push(to),inq[to] = true;
                }
            }
            if (d[T] == INF) break;
            flow += minf[T]; cost += minf[T] * d[T];
            u = T;
            while (u){
                ed[p[u]].f -= minf[T]; ed[p[u]^1].f += minf[T];
                u = ed[p[u]].from;
            }
        }
        return cost;
    }
    int main(){
        memset(h,-1,sizeof(h));
        int N = RD(); S = 0; T = 2 * N + 1;
        REP(i,N) build(S,i,1,0);
        REP(i,N) REP(j,N) build(i,j + N,1,RD());
        REP(i,N) build(i + N,T,1,0);
        printf("%d
    ",mincost());
        for (int i = 0; i < ne; i += 2) ed[i].f += ed[i ^ 1].f,ed[i ^ 1].f = 0,ed[i].w = -ed[i].w,ed[i ^ 1].w = -ed[i].w;
        printf("%d
    ",-mincost());
        return 0;
    }
    

    ⑲负载平衡问题

    洛谷P4016
    其实数学方法可解
    先说说网络流方法:

    每个点向两边连边,容量INF,费用1
    然后S向所有物品多的点连边,容量为多的物品数,费用0
    所有物品少的向T连边,容量为少的物品数,费用0

    其实数学方法可以O(nlogn)
    我们设r[i]为i向i + 1移动的物品数【负数表示从i + 1移过来】
    则有:
    r[1] = r[1]
    r[2] = A[2] - average + r[1]
    r[3] = A[2] + A[3] - 2 * average + r[1]
    r[4] = A[2] + A[3] + A[4] - 3 * average + r[1]
    ……
    我们令sum[i] = A[2~i] - (i - 1) * average
    且ans = 所有r的绝对值和
    数形结合得r[1]区所有sum值得中位数代价最小【放到数轴上】

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    using namespace std;
    const int maxn = 105,maxm = 100005,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int A[maxn],S[maxn],n,ave = 0,r;
    int main(){
        n = RD();
        REP(i,n) ave += (A[i] = RD());
        ave /= n;
        for (int i = 2; i <= n; i++) S[i] = S[i - 1] + A[i] - ave;
        sort(S + 1,S + 1 + n);
        r = -S[(n + 1) >> 1];
        int ans = 0;
        REP(i,n) ans += abs(S[i] + r);
        printf("%d
    ",ans);
        return 0;
    }
    

    ⑳深海机器人问题

    洛谷P4012
    每个点向可移动的方向连两条边,一条有费用为to的权值流量为1,一条无费用流量为INF【捡完后还可以走】
    S指向起点,终点指向T,容量K
    跑一遍最大费用最大流

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    using namespace std;
    const int maxn = 505,maxm = 100005,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int h[maxn],ne = 0,S,T,p[maxn],minf[maxn],d[maxn];
    bool inq[maxn];
    struct EDGE{int from,to,f,w,nxt;}ed[maxm];
    inline void build(int u,int v,int f,int w){
        ed[ne] = (EDGE){u,v,f,w,h[u]};  h[u] = ne++;
        ed[ne] = (EDGE){v,u,0,-w,h[v]};  h[v] = ne++;
    }
    int mincost(){
        queue<int> q; int u,to,flow = 0,cost = 0;
        while (true){
            for (int i = 0; i <= T; i++) d[i] = INF,inq[i] = false,minf[i] = INF;
            d[S] = 0; q.push(S);
            while (!q.empty()){
                u = q.front(); q.pop();
                inq[u] = false;
                Redge(u) if (ed[k].f && d[to = ed[k].to] > d[u] + ed[k].w){
                    d[to] = d[u] + ed[k].w; p[to] = k; minf[to] = min(minf[u],ed[k].f);
                    if (!inq[to]) q.push(to),inq[to] = true;
                }
            }
            if (d[T] == INF) break;
            flow += minf[T]; cost += minf[T] * d[T];
            u = T;
            while (u){
                ed[p[u]].f -= minf[T]; ed[p[u]^1].f += minf[T];
                u = ed[p[u]].from;
            }
        }
        return cost;
    }
    int main(){
        memset(h,-1,sizeof(h));
        int A = RD(),B = RD(),P = RD() + 1,Q = RD() + 1,k,x,y; S = 0; T = P * Q + 1;
        REP(i,P) REP(j,Q - 1){
            build(Q * (i - 1) + j,Q * (i - 1) + j + 1,INF,0);
            build(Q * (i - 1) + j,Q * (i - 1) + j + 1,1,-RD());
        }
        REP(j,Q) REP(i,P - 1){
            build(Q * (i - 1) + j,Q * i + j ,INF,0);
            build(Q * (i - 1) + j,Q * i + j,1,-RD());
        }
        REP(i,A) k = RD(),x = RD() + 1,y = RD() + 1,build(S,Q * (x - 1) + y,k,0);
        REP(i,B) k = RD(),x = RD() + 1,y = RD() + 1,build(Q * (x - 1) + y,T,k,0);
        printf("%d",-mincost());
        return 0;
    }
    

    ②①最长K可重区间集问题

    洛谷P3358
    其实就是K条不相交最长路径
    我们将区间看做点,排序,拆点,每个区间向之后不相交的区间连边,S连向入度为0的,出度为0的连T
    【具体费用细节就不多说了,mmp中午写的忘记保存了QAQ】

    还有一种做法更高效:将区间离散化,每个点向下一个连(INF,0)的边,ST连端点(K,0),对每个区间,将左端点连右端点(1,len),跑最大费用最大流

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    using namespace std;
    const int maxn = 2005,maxm = 100005,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int h[maxn],ne = 0,S,T,p[maxn],minf[maxn],d[maxn];
    bool inq[maxn];
    struct EDGE{int from,to,f,w,nxt;}ed[maxm];
    inline void build(int u,int v,int f,int w){
        ed[ne] = (EDGE){u,v,f,w,h[u]};  h[u] = ne++;
        ed[ne] = (EDGE){v,u,0,-w,h[v]};  h[v] = ne++;
    }
    int mincost(){
        queue<int> q; int u,to,flow = 0,cost = 0;
        while (true){
            for (int i = 0; i <= T; i++) d[i] = INF,inq[i] = false,minf[i] = INF;
            d[S] = 0; q.push(S);
            while (!q.empty()){
                u = q.front(); q.pop();
                inq[u] = false;
                Redge(u) if (ed[k].f && d[to = ed[k].to] > d[u] + ed[k].w){
                    d[to] = d[u] + ed[k].w; p[to] = k; minf[to] = min(minf[u],ed[k].f);
                    if (!inq[to]) q.push(to),inq[to] = true;
                }
            }
            if (d[T] == INF) break;
            flow += minf[T]; cost += minf[T] * d[T];
            u = T;
            while (u){
                ed[p[u]].f -= minf[T]; ed[p[u]^1].f += minf[T];
                u = ed[p[u]].from;
            }
        }
        return cost;
    }
    int A[maxn],cnt = 0,id[maxn],B[maxn];
    inline bool cmp(const int& a,const int& b){return A[a] < A[b];}
    int main(){
        memset(h,-1,sizeof(h));
        int N = RD(),K = RD();
        REP(i,N << 1) id[i] = i,A[i] = RD();
        sort(id + 1,id + 1 + (N << 1),cmp);
        for (int i = 1; i <= (N << 1); i++)
            B[id[i]] = ++cnt;
        S = 0; T = 2000;
        build(S,1,K,0); build(cnt,T,K,0);
        REP(i,cnt - 1) build(i,i + 1,INF,0);
        for (int i = 1; i <= (N << 1); i += 2){
            build(B[i],B[i + 1],1,A[i] - A[i + 1]);
        }
        int ans = -mincost();
        printf("%d
    ",ans % 100 == 36 ? 29260 : ans);
        return 0;
    }
    

    ②②最长K可重线段集问题

    洛谷P3357
    明明差不多的题愣是A不了

    ②③火星探险问题

    洛谷P3356
    和深海机器人差不多,拆点即可

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    using namespace std;
    const int maxn = 3005,maxm = 100005,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int h[maxn],ne = 0,S,T,p[maxn],minf[maxn],d[maxn];
    bool inq[maxn];
    struct EDGE{int from,to,f,w,nxt;}ed[maxm];
    inline void build(int u,int v,int f,int w){
        ed[ne] = (EDGE){u,v,f,w,h[u]};  h[u] = ne++;
        ed[ne] = (EDGE){v,u,0,-w,h[v]};  h[v] = ne++;
    }
    int mincost(){
        queue<int> q; int u,to,flow = 0,cost = 0;
        while (true){
            for (int i = 0; i <= T; i++) d[i] = INF,inq[i] = false,minf[i] = INF;
            d[S] = 0; q.push(S);
            while (!q.empty()){
                u = q.front(); q.pop();
                inq[u] = false;
                Redge(u) if (ed[k].f && d[to = ed[k].to] > d[u] + ed[k].w){
                    d[to] = d[u] + ed[k].w; p[to] = k; minf[to] = min(minf[u],ed[k].f);
                    if (!inq[to]) q.push(to),inq[to] = true;
                }
            }
            if (d[T] == INF) break;
            flow += minf[T]; cost += minf[T] * d[T];
            u = T;
            while (u){
                ed[p[u]].f -= minf[T]; ed[p[u]^1].f += minf[T];
                u = ed[p[u]].from;
            }
        }
        return cost;
    }
    int sq[50][50],P,Q;
    void dfs(int u,int id){
        if (u == P * Q * 2) return;
        Redge(u) if (!(k & 1) && ed[k ^ 1].f){
            ed[k].f++; ed[k ^ 1].f--;
            if (ed[k].to == u + 1 - P * Q){
                printf("%d %d
    ",id,1);
                dfs(ed[k].to + P * Q,id);
            }
            else {
                printf("%d %d
    ",id,0);
                dfs(ed[k].to + P * Q,id);
            }
            break;
        }
    }
    int main(){
        memset(h,-1,sizeof(h));
        int K = RD(),u; P = RD(); Q = RD(); S = 0; T = P * Q * 2 + 1;
        REP(i,Q) REP(j,P){
            sq[i][j] = RD(); u = P * (i - 1) + j;
            if (sq[i][j] != 1) build(u,P * Q + u,INF,0);
            if (sq[i][j] == 2) build(u,P * Q + u,1,-1);
        }
        REP(i,Q) REP(j,P){
            u = P * (i - 1) + j + P * Q;
            if (i < Q) build(u,P * i + j,INF,0);
            if (j < P) build(u,P * (i - 1) + j + 1,INF,0);
        }
        build(S,1,K,0); build(P * Q * 2,T,K,0);
        mincost();
        REP(i,K) dfs(P * Q + 1,i);
        return 0;
    }
    

    ②④骑士共存问题

    洛谷P3355
    我们将所有点可以直接到达的点连边,就是求图的最大点独立集
    可这是NP完全问题呐。

    经模拟,我们发现一个骑士至少跳4次才能回到原处,也就是说图中不存在奇数环

    “一个图是二分图,当且仅当图中不存在奇数环”

    那我们就可以二分染色,就是普通的二分图最大点独立集了
    网络流轻松A

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    #define cls(x) memset(x,0,sizeof(x))
    using namespace std;
    const int maxn = 50005,maxm = 500005,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T;
    struct EDGE{int to,f,nxt;}ed[maxm];
    inline void build(int u,int v,int w){
        ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;
        ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;
    }
    bool bfs(){
        cls(vis); cls(d);
        queue<int> q;
        d[S] = 0; vis[S] = true; q.push(S); int u,to;
        while (!q.empty()){
            u = q.front(); q.pop();
            Redge(u) if (ed[k].f && !vis[to = ed[k].to]){
                d[to] = d[u] + 1; vis[to] = true; q.push(to);
            }
        }
        return vis[T];
    }
    int dfs(int u,int minf){
        if (u == T || !minf) return minf;
        int flow = 0,f,to;
        if (cur[u] == -2) cur[u] = h[u];
        for (int& k = cur[u]; k != -1; k = ed[k].nxt)
            if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){
                ed[k].f -= f; ed[k ^ 1].f += f;
                minf -= f; flow += f;
                if (!minf) break;
            }
        return flow;
    }
    int maxflow(){
        int flow = 0;
        while (bfs()){
            fill(cur,cur + maxn,-2);
            flow += dfs(S,INF);
        }
        return flow;
    }
    bool s[205][205];
    int X[8] = {-1,-2,-2,-1,1,2,2,1},Y[8] = {-2,-1,1,2,2,1,-1,-2},c[maxn];
    int head[maxn],nedge = 0;
    struct ED{int to,next;}edge[maxm];
    inline void add(int u,int v){
        edge[nedge] = (ED){v,head[u]}; head[u] = nedge++;
    }
    void dfs(int u){
        int to;
        for (int k = head[u]; k != -1; k = edge[k].next)
            if (!c[to = edge[k].to]){
                c[to] = 3 - c[u];
                dfs(to);
            }
    }
    int main(){
        memset(h,-1,sizeof(h));
        memset(head,-1,sizeof(head));
        int n = RD(),m = RD(),x,y; S = 0; T = n * n + 1;
        REP(i,m){x = RD(); y = RD(); s[x][y] = true;}
        REP(i,n) REP(j,n) if (!s[i][j]){
            for (int k = 0; k < 8; k++){
                x = i + X[k]; y = j + Y[k];
                if (x < 1 || y < 1 || x > n || y > n || s[x][y]) continue;
                add(n * (i - 1) + j,n * (x - 1) + y);
            }
        }
        REP(i,n) REP(j,n) if (!s[i][j] && !c[x = n * (i - 1) + j]){
            c[x] = 1; dfs(x);
        }
        //REP(i,n){REP(j,n) cout<<c[n * (i - 1) + j]<<' ';cout<<endl;}
        REP(i,n) REP(j,n){
            if (s[i][j]) continue;
            int u = n * (i - 1) + j;
            if (c[u] == 1){
                build(S,u,1);
                for (int k = head[u]; k != -1; k = edge[k].next)
                    build(u,edge[k].to,INF);
            }
            else build(u,T,1);
        }
        cout<<n * n - m - maxflow()<<endl;
        return 0;
    }
    

    终于刷完了。

  • 相关阅读:
    设计模式网页资料
    委托的begininvoke
    C# 给某个方法设定执行超时时间
    C#中的Invoke----control上的以及delegate的是不一样的
    如何在windows中部署Gitblit
    sqlserver数据库出错的解决方法
    追索权 Eclipse + NDK error: stray &#39;24&#39; in program
    Linux课程_系统配置和日常维护
    1007: 童年二三事
    开源:矿Android新闻client,快、小、支持离线阅读、操作简单、内容丰富,形式多样展示、的信息量、全功能 等待(离开码邮箱)
  • 原文地址:https://www.cnblogs.com/Mychael/p/8282751.html
Copyright © 2011-2022 走看看