网络流24题

T1 飞行员配对方案

二分图最大匹配,这里写个匈牙利算法

#include<bits/stdc++.h>
using namespace std;
const int maxn=105;
struct edge{
    int x,y,next;
    edge(){}
    edge(int _x,int _y,int _nt):x(_x),y(_y),next(_nt){}
}e[20005];
int head[maxn],tot=0;
#define addedge(x,y) {\
    e[++tot]=edge(x,y,head[x]);head[x]=tot;\
}
int n,m;
bool vst[maxn];
int match[maxn];
int DFS(int x){
    for(int i=head[x];i;i=e[i].next){
        int y=e[i].y;
        if(vst[y])continue;vst[y]=1;
        if(!match[y] || DFS(match[y])){
            match[y]=x;
            return 1;
        }
    }
    return 0;
}
int main(){
    scanf("%d%d",&n,&m);
    int x,y;
    while(cin>>x>>y && x+y>0)addedge(x,y);

    int ans=0;
    for(int i=1;i<=n;i++){
        memset(vst,0,sizeof(vst));
        ans+=DFS(i);
    }
    printf("%d\n",ans);
}

T2 太空飞行计划

最大权闭合子图
S向实验i连一条流量为Ci的边,仪器i向T连一条流量为Ci的边,每组实验i与仪器j的关系连一条流量为INF的边
答案为所有实验的收入的和减去网络最大流(最小割)
仔细考虑割掉了一些什么样的边

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<queue>
using namespace std;
const int maxn=110;
const int maxm=10010;
const int INF=0x3f3f3f3f;
struct edge{
    int x,y,next,v;
    edge(){}
    edge(int _x,int a,int b,int c):x(_x),y(a),next(b),v(c){}
} e[maxm<<1];
int head[maxn],tot=1,h[maxn],cur[maxn];
int n,m,S,T;
void addedge(int x,int y,int v){
    e[++tot]=edge(x,y,head[x],v);head[x]=tot;
    e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
bool BFS(){
    queue<int>q;
    for(int i=1;i<=T;i++)h[i]=-1;

    h[S]=0;q.push(S);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y;int v=e[i].v;
            if(h[y]==-1 && v){
                h[y]=h[x]+1;
                q.push(y);
            }
        }
    }
    return h[T]!=-1;
}
int DFS(int x,int f){
    int tmp,used=0;
    if(x==T)return f;
    for(int i=cur[x];i;i=e[i].next){
        int y=e[i].y,v=e[i].v;
        if(h[y]==h[x]+1 && v){
            tmp=DFS(y,min(v,f-used));
            e[i].v-=tmp;if(e[i].v)cur[x]=i;
            e[i^1].v+=tmp;
            used+=tmp;
            if(used==f)return used;
        }
    }
    if(!used)h[x]=-1;
    return used;
}
int maxf(){
    int ret=0;
    while(BFS()){
        for(int i=1;i<=T;i++)cur[i]=head[i];

        ret+=DFS(S,INF);
    }
    return ret;
}
int main(){
    scanf("%d%d",&n,&m);
    S=n+m+1,T=S+1;
    int ans=0;
    for(int i=1;i<=n;i++){
        int c;
        scanf("%d",&c);
        addedge(S,i,c);
        ans+=c;
        int d;
        while(scanf("%d",&d)&&(d!=0)){
            addedge(i,d+n,INF);
        }
    }
    for(int i=1;i<=m;i++){
        int c;
        scanf("%d",&c);
        addedge(i+n,T,c);
    }



    ans-=maxf();
    for(int i=1;i<=n;i++)if(h[i]!=-1)cout<<i<<" ";cout<<endl;
    for(int i=1;i<=m;i++)
        if(h[i+n]!=-1)cout<<i<<" ";cout<<endl;
    cout<<ans<<endl;
}

T3 最小路径覆盖

求有向无环图最小路径覆盖
拆点构造二分图,原图中每个点i拆成二分图X,Y集合中的两个点Xi,Yi。对于原图中的每条边(i,j),在二分图中连边(Xi,Yi)。最小路径覆盖条数等于原图点数减去最大匹配数。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=40010;
const int maxm=80010;
const int INF=0x3f3f3f3f;
struct edge{
    int y,next,v;
    edge(){}
    edge(int a,int b,int c):y(a),next(b),v(c){}
} e[maxm<<1];

int head[maxn],tot=1;
int n,m,S,T;
void addedge(int x,int y,int v){
    e[++tot]=edge(y,head[x],v);head[x]=tot;
    e[++tot]=edge(x,head[y],0);head[y]=tot;
}
int cur[maxn],h[maxn];

#include<queue>
queue<int>q;
bool BFS(){
    memset(h,-1,sizeof(h));
    q.push(S);h[S]=0;
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y,v=e[i].v;
            if(h[y]==-1 && v){
                q.push(y);
                h[y]=h[x]+1;
            }
        }
    }
    return h[T]!=-1;
}
int mark[maxn],to[maxn];
int DFS(int x,int f){
    if(x==T)return f;
    int tmp,used=0;
    for(int i=cur[x];i;i=e[i].next){
        int y=e[i].y;
        int v=e[i].v;
        if(v && h[y]==h[x]+1){
            tmp=DFS(y,min(v,f-used));
            e[i].v-=tmp;
            e[i^1].v+=tmp;
            used+=tmp;
            if(tmp){
                to[x]=y;
                if(y>n)mark[y-n]=1;
            }
            if(e[i].v)cur[x]=i;
            if(used==f)return f;
        }
    }
    if(!used)h[x]=-1;
    return used;
}
int maxf(){
    int ret=0;
    while(BFS()){
        for(int i=1;i<=T;i++)cur[i]=head[i];
        ret+=DFS(S,INF);
    }
    return ret;
}
int main(){
    scanf("%d%d",&n,&m);

    S=n*2+1;T=S+1;
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x,y+n,1);
    }
    for(int i=1;i<=n;i++){
        addedge(S,i,1);
        addedge(i+n,T,1);
    }
    memset(to,0,sizeof(to));
    memset(mark,0,sizeof(mark));
    printf("%d\n",n-maxf());
    for(int i=1;i<=n;i++){
        if(mark[i])continue;
        printf("%d",i);
        int y=i;
        while(to[y]){
            printf(" %d",to[y]-n);
            y=to[y]-n;
        }
        printf(" 0\n");
    }
}

T4 魔术球问题

枚举答案+最小路径覆盖

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
using namespace std;
const int maxn=4010;
const int maxm=200010;
const int INF=0x3f3f3f3f;
const int offset=2000;
struct edge{
    int x,y,next,v;
    edge(){}
    edge(int _x,int a,int b,int c):x(_x),y(a),next(b),v(c){}
} e[maxm<<1];

int head[maxn],tot=1;
void addedge(int x,int y,int v){
    e[++tot]=edge(x,y,head[x],v);head[x]=tot;
    e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
bool issquare[maxn];
int S,T,n;
#include<queue>
queue<int>q;
int h[maxn];
bool BFS(){
    memset(h,-1,sizeof(h));
    h[S]=0;q.push(S);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y;
            int v=e[i].v;
            if(v && h[y]==-1){
                h[y]=h[x]+1;
                q.push(y);
            }
        }
    }
    return h[T]!=-1;
}
int DFS(int x,int f){
    if(x==T)return f;
    int used=0,tmp;
    for(int i=head[x];i;i=e[i].next){
        int y=e[i].y;
        int v=e[i].v;
        if(v && h[y]==h[x]+1){
            tmp=DFS(y,min(v,f-used));
            e[i].v-=tmp;
            e[i^1].v+=tmp;
            used+=tmp;
            if(used==f)return f;
        }
    }
    if(!used)h[x]=-1;
    return used;
}
int ret=0;
int maxf(){

    while(BFS())ret+=DFS(S,INF);
    return ret;
}
int main(){
    scanf("%d",&n);
    memset(issquare,0,sizeof(issquare));
    for(int i=1;i<=60;i++)issquare[i*i]=1;
    S=0;T=maxn-1;
    addedge(S,1,1);
    addedge(1+offset,T,1);
    int tmp,A;
    for(A=1;A-maxf() <= n;){
        ++A;
        addedge(S,A,1);
        addedge(A+offset,T,1);
        for(int j=1;j<A;j++){
            if(issquare[A+j])
                addedge(j,A+offset,1);
        }
    }
    int ANS=A-1;
    printf("%d\n",ANS);
}

T5 圆桌问题

二分图多重匹配

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<cstdlib>
using namespace std;
const int maxn=2010;
const int maxm=400010;
const int INF=0x3f3f3f3f;
struct edge{
    int x,y,next,v;
    edge(){}
    edge(int _x,int _y,int _nt,int _v):
        x(_x),y(_y),next(_nt),v(_v){}
} e[maxm];
int head[maxn],tot=1;
void addedge(int x,int y,int v){
    e[++tot]=edge(x,y,head[x],v);head[x]=tot;
    e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
int cur[maxn],h[maxn];
int n,m;
int a[maxn],b[maxn];
int S,T;
queue<int>q;
bool BFS(){
    memset(h,-1,sizeof(h));
    q.push(S);h[S]=0;
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y;
            int v=e[i].v;
            if( v && h[y]==-1){
                h[y]=h[x]+1;
                q.push(y);
            }
        }
    }
    return h[T]!=-1;
}
int DFS(int x,int f){
    if(x==T)return f;
    int used=0,tmp;
    for(int i=cur[x];i;i=e[i].next){
        int y=e[i].y;
        int v=e[i].v;
        if(v && h[y]==h[x]+1){
            tmp=DFS(y,min(v,f-used));
            e[i].v-=tmp;
            e[i^1].v+=tmp;
            used+=tmp;
            if(e[i].v)cur[x]=i;
            if(used==f)return f;
        }
    }
    if(!used)h[x]=-1;
    return used;
}
int maxf(){
    int ret=0;
    while(BFS()){
        for(int i=S;i<=T;i++)cur[i]=head[i];
        ret+=DFS(S,INF);
    }
    return ret;
}
int main(){
    scanf("%d%d",&n,&m);
    S=0;T=n+m+1;
    int sum=0;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=m;i++)scanf("%d",&b[i]);
    for(int i=1;i<=n;i++)sum+=a[i];
    for(int i=1;i<=n;i++)
        addedge(S,i,a[i]);
    for(int i=1;i<=m;i++)
        addedge(i+n,T,b[i]);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
        addedge(i,n+j,1);

    int tmp=maxf();
    if(tmp == sum)puts("1");else puts("0");
}

T6 最长递增子序列

DP+最大流

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<cstdlib>
using namespace std;
const int maxn=2010;
const int maxm=400010;
const int INF=0x3f3f3f3f;
struct edge{
    int x,y,next,v;
    edge(){}
    edge(int a,int b,int c,int d):x(a),y(b),next(c),v(d){}
}e[maxm];
int head[maxn],tot=1;
int S,T;
void addedge(int x,int y,int v){
    e[++tot]=edge(x,y,head[x],v);head[x]=tot;
    e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
int h[maxn],cur[maxn];
int a[maxn],f[maxn],n;

queue<int>q;
bool BFS(){
    memset(h,-1,sizeof(h));
    h[S]=0;q.push(S);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y;
            int v=e[i].v;
            if(h[y]==-1 && v){
                h[y]=h[x]+1;
                q.push(y);
            }
        }
    }
    return h[T]!=-1;
}
int DFS(int x,int f){
    if(x==T)return f;
    int used=0,tmp;
    for(int i=cur[x];i;i=e[i].next){
        int y=e[i].y;
        int v=e[i].v;
        if(h[y]==h[x]+1 && v){
            tmp=DFS(y,min(v,f-used));
            e[i].v-=tmp;
            e[i^1].v+=tmp;
            used+=tmp;
            if(e[i].v)cur[x]=i;
            if(used==f)return f;
        }
    }
    if(!used)h[x]=-1;
    return used;
}
int ret=0;
int maxf(){
    while(BFS()){
        for(int i=S;i<=T;i++)cur[i]=head[i];
        ret+=DFS(S,INF);
    }
    return ret;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        f[i]=1;
    }
    for(int i=n-1;i>=1;i--){
        for(int j=i+1;j<=n;j++)
            if(a[i]<a[j])f[i]=max(f[i],f[j]+1);
    }
    int ans1=0;
    for(int i=1;i<=n;i++)ans1=max(ans1,f[i]);
    printf("%d\n",ans1);

    memset(head,0,sizeof(head));
    tot=1;
    S=0;T=n*2+8;

    for(int i=1;i<=n;i++){
        addedge(i,i+n,1);
        if(f[i]==ans1)addedge(S,i,1);
        if(f[i]==1) addedge(i+n,T,1);
        for(int j=i+1;j<=n;j++)
        if(f[i]==f[j]+1 && a[i]<a[j])
            addedge(i+n,j,1);
    }

    printf("%d\n",maxf());

    addedge(1,1+n,INF);
    addedge(n,n+n,INF);
    if(f[1]==ans1)addedge(S,1,INF);
    addedge(n+n,T,INF);

    printf("%d\n",maxf());
}

T7 试题库问题

二分图多重匹配

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<queue>
using namespace std;
const int maxn=2010;
const int maxm=400010;
const int INF=0x3f3f3f3f;
struct edge{
    int x,y,next,v;
    edge(){}
    edge(int a,int b,int c,int d)
        :x(a),y(b),next(c),v(d){}
} e[maxm];
int head[maxn],tot=1;
void addedge(int x,int y,int v){
    e[++tot]=edge(x,y,head[x],v);head[x]=tot;
    e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
int S,T;
int n,m,k;
int cur[maxn],h[maxn];
queue<int>q;
bool BFS(){
    memset(h,-1,sizeof(h));
    h[S]=0;q.push(S);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y;
            int v=e[i].v;
            if(v && h[y]==-1){
                h[y]=h[x]+1;
                q.push(y);
            }
        }
    }
    return h[T]!=-1;
}
int DFS(int x,int f){
    if(x==T)return f;
    int used=0,tmp;
    for(int i=cur[x];i;i=e[i].next){
        int y=e[i].y;
        int v=e[i].v;
        if(v && h[y]==h[x]+1){
            tmp=DFS(y,min(v,f-used));
            e[i].v-=tmp;
            e[i^1].v+=tmp;
            used+=tmp;
            if(e[i].v)cur[x]=i;
            if(used==f)return f;
        }
    }
    if(!used)h[x]=-1;
    return used;
}
int ret=0;
int maxf(){
    while(BFS()){
        for(int i=S;i<=T;i++)cur[i]=head[i];
        ret+=DFS(S,INF);
    }
    return ret;
}

int main(){
    scanf("%d%d",&k,&n);
    S=0;T=n+k+1;m=0;
    for(int i=1;i<=k;i++){
        int v;
        scanf("%d",&v);
        m+=v;
        addedge(n+i,T,v);
    }
    for(int i=1;i<=n;i++){
        int p,y;
        addedge(S,i,INF);
        scanf("%d",&p);
        for(int j=1;j<=p;j++){
            scanf("%d",&y);
            addedge(i,y+n,1);
        }
    }
    if(maxf()>=m)puts("YES");
    else puts("No Solution!");
}

T9 方格取数问题

二分图最大点权独立集
对格子黑白染色,相邻格子连容量正无穷的边,S向白格连流量为格中数值的边,黑格向T连流量为格中数值的边。求最小割,答案为所有格子数值之和减去最小格
最大点权独立集=点权和-最小点权覆盖集=点权和-最小割集

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<queue>
using namespace std;
const int maxn=510;
const int maxm=1000010;
const int INF=0x3f3f3f3f;

struct edge{
    int x,y,next,v;
    edge(){}
    edge(int a,int b,int c,int d)
        :x(a),y(b),next(c),v(d){}
} e[maxm];
int head[maxn],tot=1;
void addedge(int x,int y,int v){
    e[++tot]=edge(x,y,head[x],v);head[x]=tot;
    e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
int S,T,n,m;
int cur[maxn],h[maxn];
int ret=0;
queue<int>q;
bool BFS(){
    memset(h,-1,sizeof(h));
    h[S]=0;q.push(S);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y;
            int v=e[i].v;
            if(h[y]==-1 && v){
                h[y]=h[x]+1;
                q.push(y);
            }
        }
    }
    return h[T]!=-1;
}
int DFS(int x,int f){
    if(x==T)return f;
    int used=0,tmp;
    for(int i=cur[x];i;i=e[i].next){
        int y=e[i].y;
        int v=e[i].v;
        if(h[y]==h[x]+1 && v){
            tmp=DFS(y,min(v,f-used));
            e[i].v-=tmp;
            e[i^1].v+=tmp;
            used+=tmp;
            if(e[i].v)cur[x]=i;
            if(used==f)return f;
        }
    }
    if(!used)h[x]=-1;
    return used;
}
int maxf(){
    while(BFS()){
        for(int i=S;i<=T;i++)cur[i]=head[i];
        ret+=DFS(S,INF);
    }
    return ret;
}

const int mx[]={0,1,-1,0};
const int my[]={1,0,0,-1};
int num(int x,int y){
    return (x-1)*m+y;
}
int main(){

    scanf("%d%d",&n,&m);
    S=0;T=n*m+1;
    int sum=0;

    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++){
        int v;
        scanf("%d",&v);
        sum+=v;
        int id=num(i,j);
        if((i+j)%2==0){
            addedge(S,id,v);
            for(int k=0;k<4;k++){
                int nx=i+mx[k];
                int ny=j+my[k];
                if(nx >=1 && nx<=n && ny>=1 && ny<=m){
                    addedge(id,num(nx,ny),INF);
                }
            }
        }
        else
            addedge(id,T,v);
    }
    printf("%d\n",sum-maxf());
}

T10 餐巾问题

供求平衡问题,费用流解决

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 2010;
const int maxm = 4000010;
const int INF = 0x3f3f3f3f;
struct edge{
    int x,y,next,v,c;
    edge(){}
    edge(int _x,int _y,int _nt,int _v,int _c)
        :x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[maxm];
int head[maxn],tot=1,dis[maxn],fro[maxn],a[maxn];
bool inq[maxn];
int S,T,n,m,p,f,tt,ss;
void addedge(int x,int y,int v,int c){
    e[++tot]=edge(x,y,head[x],v,c);head[x]=tot;
    e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;
}
queue<int>q;
bool SPFA(){
    for(int i=S;i<=T;i++){
        dis[i]=INF;
        inq[i]=0;
    }
    q.push(S);inq[S]=1;
    dis[S]=0;
    while(!q.empty()){
        int x=q.front();q.pop();
        inq[x]=0;
        // cout<<x<<":"<<endl;;
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y;
            // cout<<y<<endl;
            int v=e[i].v;
            int c=e[i].c;
            if(v && dis[x]+c<dis[y]){
                dis[y]=dis[x]+c;
                fro[y]=i;
                if(!inq[y]){
                    inq[y]=1;
                    q.push(y);
                }
            }
        }
    }
    return dis[T]!=INF;
}
int mcf(){
    int ret=0;
    while(SPFA()){
        int tmp=INF;
        for(int i=fro[T];i;i=fro[e[i].x]){
            tmp=min(tmp,e[i].v);
        }
        ret+=tmp*dis[T];
        for(int i=fro[T];i;i=fro[e[i].x]){
            e[i].v-=tmp;
            e[i^1].v+=tmp;
        }
    }
    return ret;
}
int main(){
    scanf("%d%d%d%d%d%d",&n,&p,&m,&f,&tt,&ss);

    for(int i=1;i<=n;i++)scanf("%d",&a[i]);

    S=0;T=n*2+4;
    for(int i=1;i<=n;i++){
        addedge(S,i,a[i],0);
        addedge(i+n,T,a[i],0);
        addedge(S,i+n,INF,p);
        if(i+1<=n)addedge(i,i+1,INF,0);
        if(i+m<=n)addedge(i,i+m+n,INF,f);
        if(i+tt<=n)addedge(i,i+tt+n,INF,ss);
    }
    printf("%d\n",mcf());

}

T11 航空路线问题

最大费用最大流求两条不相交路径
拆点,每个城市i拆为Ai和Bi两个点,Ai到Bi连流量为1费用为1的边,A1到B1,An到Bn连流量为2费用为1的边。
对于给出的边(i,j),连Bi-Aj流量为1,费用为0。
求最大费用最大流mcf,若A1-B1、An-Bn满流,则有解为mcf-2,否则无解。

#include<bits/stdc++.h>
using namespace std;
#define MADOKA main
int hash(char *str){
    int ret=0,seed=131;
    for(;*str;ret=ret*seed+*str++);
    return ret & 0x7fffffff;
}
map<int,int>mp;
struct edge{
    int x,y,next,v,c;
    edge(){}
    edge(int _x,int _y,int _nt,int _v,int _c):x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[20005];
int head[105],tot=1,dis[105],fro[105],a[105];
bool inq[105];
queue<int>q;
char str[1000];
#define addedge(x,y,v,c) {\
    e[++tot]=edge(x,y,head[x],v,c);head[x]=tot;\
    e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;\
}
int S,T,n,m;
bool SPFA(){
    for(int i=S;i<=T;i++)dis[i]=inq[i]=0;
    q.push(S);inq[S]=1;
    dis[S]=0;
    while(!q.empty()){
        int x=q.front();q.pop();
        inq[x]=0;
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y;
            int v=e[i].v;
            int c=e[i].c;
            if(v && dis[x]+c>dis[y]){
                dis[y]=dis[x]+c;
                fro[y]=i;
                if(!inq[y]){
                    inq[y]=1;q.push(y);
                }
            }
        }
    }
    return dis[T]!=0;
}
int mcf(){
    int ret=0;
    while(SPFA()){
        int tmp=100;
        for(int i=fro[T];i;i=fro[e[i].x])tmp=min(tmp,e[i].v);
        ret+=tmp*dis[T];
        for(int i=fro[T];i;i=fro[e[i].x]){
            e[i].v-=tmp;
            e[i^1].v+=tmp;
        }
    }
    return ret;
}
int MADOKA(){
    // freopen("input.txt","r",stdin);
    // freopen("output.txt","w",stdout);

    scanf("%d%d",&n,&m);
    S=1;T=n*2;
    for(int i=1;i<=n;i++){
        scanf("%s",str);
        mp[hash(str)]=i;
        if(i==1 || i==n){
            addedge(i,i+n,2,1);
        }else{
            addedge(i,i+n,1,1);
        }

    }
    for(int x,y,i=1;i<=m;i++){
        scanf("%s",str);x=mp[hash(str)];
        scanf("%s",str);y=mp[hash(str)];
        if(x==1 && y==n){
            addedge(x+n,y,2,0);continue;
        }
        if(x==n && y==1){
            addedge(y+n,x,2,0);continue;
        }
        if(x < y){
            addedge(x+n,y,1,0);
        }else{
            addedge(y+n,x,1,0);
        }
    }
    int ans=mcf()-2;
    if(e[2].v==0 && e[n<<1].v==0)
        printf("%d\n",ans);
    else puts("No Solution!");

}

T13 星际转移问题

分层图网络流
太空船为边,以天数分层,从天数为0开始枚举加边求最大流直到最大流达到K为止

#include<bits/stdc++.h>
using namespace std;
#define MADOKA main
const int maxn = 100005;
const int INF  = 0x3f3f3f3f;
int f[maxn];
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}

struct edge{
    int x,y,next,v;
    edge(){}
    edge(int _x,int _y,int _nt,int _v)
        :x(_x),y(_y),next(_nt),v(_v){}
}e[maxn<<1];
int head[maxn],tot=1,n,m,k,S,T,DAY,cur[maxn],h[maxn];
int maxf=0;
void addedge(int x,int y,int v){
    e[++tot]=edge(x,y,head[x],v);head[x]=tot;
    e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
queue<int>q;
bool BFS(){
    memset(h,-1,sizeof(h));
    q.push(S);h[S]=0;
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int y,v,i=head[x];i;i=e[i].next){
            y=e[i].y;v=e[i].v;
            if(v && h[y]==-1){
                h[y]=h[x]+1;
                q.push(y);
            }
        }
    }
    return h[T]!=-1;
}
int DFS(int x,int f){
    if(x==T)return f;
    int used=0,tmp;
    for(int y,v,i=cur[x];i;i=e[i].next){
        y=e[i].y;v=e[i].v;
        if(v && h[y]==h[x]+1){
            tmp=DFS(y, min(v,f-used));
            e[i].v-=tmp;
            e[i^1].v+=tmp;
            used+=tmp;
            if(e[i].v)cur[x]=i;
            if(used==f)return f;
        }
    }
    if(!used)h[x]=-1;
    return used;
}
void dinic(){
    while(BFS()){
        for(int i=S;i<=T;i++)cur[i]=head[i];
        maxf+=DFS(S,INF);
    }
}
struct ship{
    int p,r;
    int s[25];
}p[maxn];
int getnum(int N,int DAY){
    return DAY*(n+2)+N;
}
int MADOKA(){
    scanf("%d%d%d",&n,&m,&k);

    for(int i=1;i<=n+2;i++)f[i]=i;
    S=0;T=20005;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&p[i].p,&p[i].r);
        int c;
        for(int j=1;j<=p[i].r;j++){
            scanf("%d",&c);

            if(c== 0)c=n+1;
            if(c==-1)c=n+2;
            p[i].s[j]=c;
            if(j>1){
                int x=p[i].s[j-1];
                int y=p[i].s[j];
                x=find(x);y=find(y);
                f[x]=y;
            }
        }
    }
    if(find(n+1)!=find(n+2)){puts("0");return 0;}

    addedge(S,getnum(n+1,0),INF);
    addedge(getnum(n+2,0),T,INF);
    int DAY;maxf=0;
    for(DAY=1;maxf<k;DAY++){
        addedge(S,getnum(n+1,DAY),INF);
        addedge(getnum(n+2,DAY),T,INF);
        for(int i=1;i<=n+2;i++)
            addedge(getnum(i,DAY-1),getnum(i,DAY),INF);
        for(int i=1;i<=m;i++){
            int x=p[i].s[ (DAY-1) % p[i].r +1 ];
            int y=p[i].s[ ( DAY ) % p[i].r +1 ];
            addedge(getnum(x,DAY-1),getnum(y,DAY),p[i].p);
        }
        dinic();
    }
    printf("%d\n",DAY-1);
}

T14 孤岛营救

分层图最短路
dis[p,x]记录钥匙获得情况为p,当前到达x号点的最小时间,BFS转移即可

#include<bits/stdc++.h>
using namespace std;
const int maxn = 20;
const int INF = 0x3f3f3f3f;
int n,m,p,k,s;
const int mx[]={1,0,-1,0};
const int my[]={0,1,0,-1};
int g[255][255];
vector<int> key[20][20];
int dis[3200][255];
typedef pair<int,int>pii;
queue<pii>q;
int encode(int x,int y){
    return (x-1)*m+y;
}
void decode(int d,int &x,int &y){
    y=d%m;if(y==0)y=m;
    x=(d-y)/m+1;
}
int main(){
    scanf("%d%d%d",&n,&m,&p);
    scanf("%d",&k);
    memset(g,0,sizeof(g));
    for(int i=1;i<=k;i++){
        int x1,y1,x2,y2,G;
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&G);
        int p=encode(x1,y1),q=encode(x2,y2);
        g[ p ][ q ]=g[ q ][ p ]=G?G:INF;
    }
    scanf("%d",&s);
    memset(key,0,sizeof(key));
    for(int i=1;i<=s;i++){
        int x,y,Q;
        scanf("%d%d%d",&x,&y,&Q);
        key[x][y].push_back(Q);
    }
    memset(dis,-1,sizeof(dis));

    for(int i=1;i<=n*m;i++)
    for(int j=1;j<=n*m;j++){
        if(i==j)continue;
        int x1,y1,x2,y2;
        if(g[i][j]==0)continue;
        decode(i,x1,y1);decode(j,x2,y2);
    }

    int tmp=1;
    for(int i=0;i<key[1][1].size();i++)tmp|=1<<key[1][1][i];
    dis[ tmp ][ encode(1,1) ]=0;
    q.push(pii(tmp,encode(1,1)));

    while(!q.empty()){
        pii u=q.front();q.pop();
        int x=u.second,p=u.first;
        int tx,ty;
        decode(x,tx,ty);

        if(tx==n && ty==m){
            cout<<dis[p][x]<<endl;
            return 0;
        }
        for(int i=0;i<4;i++){
            int nx=tx+mx[i];
            int ny=ty+my[i];
            if(nx<1 || nx>n || ny<1 || ny>m)continue;

            int y=encode(nx,ny);

            if(g[x][y]==INF)continue;

            if(g[x][y]==0 || (p & (1<<g[x][y]))){
                int np=p;
                for(int j=0;j<key[nx][ny].size();j++)
                    np|=(1<<key[nx][ny][j]);
                if(dis[np][y]==-1){
                    dis[np][y]=dis[p][x]+1;
                    q.push(pii(np,y));
                }
            }
        }
    }
    puts("-1");
}

T15 汽车加油行驶

分层图最短路

#include<bits/stdc++.h>
using namespace std;
const int maxn = 200005;
const int INF= 0x3f3f3f3f;

int N,K,A,B,C;
int g[105][105],cnt=0;
int num(int x,int y,int h){
    return h*N*N + (x-1) * N +y;
}
struct edge{
    int x,y,next,c;
    edge(){}
    edge(int _x,int _y,int _nt,int _c):x(_x),y(_y),next(_nt),c(_c){}
}e[maxn<<2];
int head[maxn],tot=1,dis[maxn];
void addedge(int x,int y,int c){
    e[++tot]=edge(x,y,head[x],c);head[x]=tot;
}
const int mx[]={1,0,-1,0};
const int my[]={0,1,0,-1};
typedef pair<int,int>pii;
struct heap{
    pii data[maxn<<2];
    int siz;
    public:
        heap(){siz=0;}
        void push(pii x);
        void pop();
        pii top(){return data[1];}
        bool empty(){return siz==0;}
};
heap q;
void Dijkstra(){
    int S=num(1,1,K);
    int T=num(N,N,K)+1;
    for(int i=1;i<=T;i++)dis[i]=i==S?0:INF;
    q.push(pii(dis[S],S));
    while(!q.empty()){
        pii tmp=q.top();q.pop();
        int x=tmp.second;
        if(tmp.first > dis[x])continue;
        for(int y,c,i=head[x];i;i=e[i].next){
            y=e[i].y;c=e[i].c;
            if(dis[y] > dis[x]+c){
                dis[y]=dis[x]+c;
                q.push(pii(dis[y],y));
            }
        }
    }
}
int main(){
    // freopen("trav.in","r",stdin);
    // freopen("trav2.out","w",stdout);

    scanf("%d%d%d%d%d",&N,&K,&A,&B,&C);
    for(int i=1;i<=N;i++)for(int j=1;j<=N;j++){
        scanf("%d",&g[i][j]);
    }

    for(int i=1;i<=N;i++)
    for(int j=1;j<=N;j++)
    for(int p=0;p<=K;p++){
        int c=g[i][j]?A:A+C;
        if(p<K)addedge(num(i,j,p),num(i,j,K),c);
        if( (!g[i][j] && p>0) || p==K){
            for(int q=0;q<4;q++){
                int x=i+mx[q];
                int y=j+my[q];
                c=q<2 ? 0 : B;
                if(x>=1 && x<=N && y>=1 && y<=N)
                    addedge(num(i,j,p),num(x,y,p-1),c);
            }
        }
    }
    Dijkstra();
    int ans=INF;
    for(int i=0;i<=K;i++)ans=min(ans,dis[num(N,N,i)]);
    printf("%d\n",ans);
}

void heap::push(pii x){
    data[++siz]=x;int now=siz;
    while(now>>1){
        if(data[now].first<data[now>>1].first){
            swap(data[now],data[now>>1]);
            now>>=1;
        }else break;
    }
}
void heap::pop(){
    data[1]=data[siz--];int now=1,next;
    while(now<<1 < siz){
        if(data[now<<1].first<data[now<<1|1].first)
            next=now<<1;else next=now<<1|1;
        if(data[now].first>data[next].first){
            swap(data[now],data[next]);now=next;
        }else break;
    }
}

T16 数字梯形问题

费用流求不相交路径

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn = 50;
const int INF = 0x3f3f3f3f;
int n,m,cnt;
int a[maxn][maxn],num[maxn][maxn];
struct edge{
    int x,y,next,v,c;
    edge(){}
    edge(int _x,int _y,int _nt,int _v,int _c):x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[maxn<<6];
int head[maxn*maxn*2],tot,fro[maxn*maxn*2],S,T;
LL dis[maxn*maxn*2];
bool inq[maxn*maxn*2];
queue<int>q;
inline void addedge(int x,int y,int v,int c){
    e[++tot]=edge(x,y,head[x],v,c);head[x]=tot;
    e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;
    // cout<<tot<<endl;
}
bool SPFA(){
    for(int i=S;i<=T;i++)dis[i]=-INF;
    dis[S]=0;
    q.push(S);inq[S]=1;
    while(!q.empty()){
        int x=q.front();q.pop();inq[x]=0;
        for(int y,v,c,i=head[x];i;i=e[i].next){
            y=e[i].y;v=e[i].v;c=e[i].c;
            if(v && dis[x]+c > dis[y]){
                dis[y]=dis[x]+c;
                fro[y]=i;
                if(!inq[y]){
                    q.push(y);inq[y]=1;
                }
            }
        }
    }
    return dis[T]!=-INF;
}
LL mcf(){
    LL ret=0;
    while(SPFA()){
        int tmp=INF;
        for(int i=fro[T];i;i=fro[e[i].x])tmp=min(tmp,e[i].v);
        ret+=tmp*dis[T];
        for(int i=fro[T];i;i=fro[e[i].x])e[i].v-=tmp,e[i^1].v+=tmp;
    }
    return ret;
}
int main(){
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m+i-1;j++)
        scanf("%d",&a[i][j]);
    cnt=0;
    for(int i=1;i<=n;i++)for(int j=1;j<=m+i-1;j++)num[i][j]=++cnt;
    S=0;T=cnt*2+1;
    /*PROBLEM 1*/
    memset(head,0,sizeof(head));tot=1;memset(fro,0,sizeof(fro));
    for(int i=1;i<=m;i++)addedge(S,num[1][i],1,0);
    for(int i=1;i<=m+n-1;i++){addedge(num[n][i]+cnt,T,1,0);addedge(num[n][i],num[n][i]+cnt,1,a[n][i]);}
    for(int i=1;i<n;i++)for(int j=1;j<=m+i-1;j++){
        addedge(num[i][j],num[i][j]+cnt,1,a[i][j]);
        addedge(num[i][j]+cnt,num[i+1][j],1,0);
        addedge(num[i][j]+cnt,num[i+1][j+1],1,0);
    }
    // for(int i=2;i<=tot;i+=2)cout<<e[i].x<<" "<<e[i].y<<" "<<e[i].v<<" "<<e[i].c<<endl;
    printf("%lld\n",mcf());

    /*PROBLEM 2*/
    memset(head,0,sizeof(head));tot=1;memset(fro,0,sizeof(fro));
    for(int i=1;i<=m;i++)addedge(S,num[1][i],1,0);
    for(int i=1;i<=m+n-1;i++){addedge(num[n][i]+cnt,T,INF,0);addedge(num[n][i],num[n][i]+cnt,INF,a[n][i]);}
    for(int i=1;i<n;i++)for(int j=1;j<=m+i-1;j++){
        addedge(num[i][j],num[i][j]+cnt,INF,a[i][j]);
        addedge(num[i][j]+cnt,num[i+1][j],1,0);
        addedge(num[i][j]+cnt,num[i+1][j+1],1,0);
    }
    printf("%lld\n",mcf());
    /*PROBLEM 3*/
    memset(head,0,sizeof(head));tot=1;memset(fro,0,sizeof(fro));
    for(int i=1;i<=m;i++)addedge(S,num[1][i],1,0);
    for(int i=1;i<=m+n-1;i++){
        addedge(num[n][i]+cnt,T,INF,0);
        addedge(num[n][i],num[n][i]+cnt,INF,a[n][i]);
    }
    for(int i=1;i<n;i++)for(int j=1;j<=m+i-1;j++){
        addedge(num[i][j],num[i][j]+cnt,INF,a[i][j]);
        addedge(num[i][j]+cnt,num[i+1][j],INF,0);
        addedge(num[i][j]+cnt,num[i+1][j+1],INF,0);
    }
    printf("%lld\n",mcf());
}
/*
2 5
2 3
3 4 5
9 10 9 1
1 1 10 1 1
1 1 10 12 1 1

66
75
77
*/

T17 运输问题

仓库作为二分图X集,零售商店作为Y集,S向Xi连容量ai费用0的边,Yi向T连容量bi费用0的边,Xi向Yi连容量INF费用Cij的边。求最小费用最大流和最大费用最大流即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 205;
const int maxm = 50005;
const int INF  = 0x3f3f3f3f;
struct edge{
    int x,y,next,v,c;
    edge(){}
    edge(int _x,int _y,int _nt,int _v,int _c)
        :x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[maxm<<1];
int head[maxn],tot=1;
int fro[maxn],dis[maxn];bool inq[maxn];
void addedge(int x,int y,int v,int c){
    e[++tot]=edge(x,y,head[x],v, c);head[x]=tot;
    e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;
}
int n,m,S,T;
int a[maxn],b[maxn],g[105][105];
bool SPFA(int fg);
int mcf(int fg);
void build(){
    tot=1;memset(head,0,sizeof(head));
    for(int i=1;i<=m;i++)addedge(S,i,a[i],0);
    for(int i=1;i<=n;i++)addedge(i+m,T,b[i],0);
    for(int i=1;i<=m;i++)for(int j=1;j<=n;j++)
        addedge(i,j+m,INF,g[i][j]);

    // for(int i=2;i<=tot;i+=2)
        // cout<<e[i].x<<" "<<e[i].y<<" "<<e[i].v<<" "<<e[i].c<<endl;
}
int main(){ 
    scanf("%d%d",&m,&n);
    S=0;T=n+m+1;
    for(int i=1;i<=m;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)scanf("%d",&b[i]);
    for(int i=1;i<=m;i++)for(int j=1;j<=n;j++)scanf("%d",&g[i][j]);

    build();
    printf("%d\n",mcf(1));
    build();
    printf("%d\n",mcf(-1));
}

queue<int>q;
bool SPFA(int fg){
    memset(inq,0,sizeof(inq));
    for(int i=S;i<=T;i++)dis[i]=INF*fg;
    dis[S]=0;q.push(S);inq[S]=1;
    while(!q.empty()){
        int x=q.front();q.pop();inq[x]=0;
        for(int v,c,y,i=head[x];i;i=e[i].next){
            y=e[i].y;v=e[i].v;c=e[i].c;
            if(v && dis[y]*fg > (dis[x]+c)*fg){
                dis[y]=dis[x]+c;
                fro[y]=i;
                if(!inq[y]){
                    inq[y]=1;
                    q.push(y);
                }
            }
        }
    }
    return dis[T]!=INF*fg;
}
int mcf(int fg){
    int ret=0;
    memset(fro,0,sizeof(fro));
    while(SPFA(fg)){
        int tmp=INF;
        for(int i=fro[T];i;i=fro[e[i].x])tmp=min(tmp,e[i].v);
        ret+=tmp*dis[T];
        for(int i=fro[T];i;i=fro[e[i].x])e[i].v-=tmp,e[i^1].v+=tmp;
    }
    return ret;
}

T18 分配问题

建图方法与T17类似。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 205;
const int maxm = 50005;
const int INF  = 0x3f3f3f3f;
struct edge{
    int x,y,next,v,c;
    edge(){}
    edge(int _x,int _y,int _nt,int _v,int _c)
        :x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[maxm];
int tot,head[maxn],S,T,n,fro[maxn],dis[maxn];
int g[105][105];
bool inq[maxn];
inline void addedge(int x,int y,int v,int c){
    e[++tot]=edge(x,y,head[x],v, c);head[x]=tot;
    e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;
}
bool SPFA(int fg);
int mcf(int fg);
void build(){
    tot=1;memset(head,0,sizeof(head));
    S=0;T=n*2+1;
    for(int i=1;i<=n;i++){
        addedge(S,i,1,0);
        addedge(i+n,T,1,0);
    }
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
        addedge(i,j+n,INF,g[i][j]);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&g[i][j]);

    build();
    printf("%d\n",mcf(1));
    build();
    printf("%d\n",mcf(-1));
}
queue<int>q;
bool SPFA(int fg){
    for(int i=S;i<=T;i++)dis[i]=INF*fg;
    dis[S]=0;inq[S]=1;q.push(S);
    while(!q.empty()){
        int x=q.front();q.pop();inq[x]=0;
        for(int y,c,v,i=head[x];i;i=e[i].next){
            y=e[i].y;v=e[i].v;c=e[i].c;
            if(v && dis[y]*fg > (dis[x]+c)*fg ){
                dis[y]=dis[x]+c;
                fro[y]=i;
                if(!inq[y]){
                    inq[y]=1;q.push(y);
                }
            }
        }
    }
    return dis[T]!=INF*fg;
}
int mcf(int fg){
    int ret=0;
    memset(fro,0,sizeof(fro));
    while(SPFA(fg)){
        int tmp=INF;
        for(int i=fro[T];i;i=fro[e[i].x])tmp=min(tmp,e[i].v);
        ret+=tmp*dis[T];
        for(int i=fro[T];i;i=fro[e[i].x])e[i].v-=tmp,e[i^1].v+=tmp;
    }
    return ret;
}

T19 负载平衡问题

供求平衡问题,费用流解决

#include<bits/stdc++.h>
using namespace std;
const int maxn = 500;
const int INF  = 0x3f3f3f3f;
struct edge{
    int x,y,next,v,c;
    edge(){}
    edge(int _x,int _y,int _nt,int _v,int _c):x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[ 2400 ];
int head[maxn],tot=1;
int dis[maxn],fro[maxn],a[maxn];
bool inq[maxn];
queue<int>q;
int S,T,n;
inline void addedge(int x,int y,int v,int c){
    e[++tot]=edge(x,y,head[x],v,c);head[x]=tot;
    e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;
}
bool SPFA(){
    for(int i=S;i<=T;i++)dis[i]=INF,inq[i]=0;
    q.push(S);inq[S]=1;
    dis[S]=0;
    while(!q.empty()){
        int x=q.front();q.pop();
        inq[x]=0;
        for(int y,v,c,i=head[x];i;i=e[i].next){
            y=e[i].y;v=e[i].v;c=e[i].c;
            if(v && dis[x]+c<dis[y]){
                dis[y]=dis[x]+c;fro[y]=i;
                if(!inq[y]){
                    inq[y]=1;q.push(y);
                }
            }
        }
    }
    return dis[T]!=INF;
}
int mcf(){
    int ret=0;
    while(SPFA()){
        int tmp=INF;
        for(int i=fro[T];i;i=fro[e[i].x])tmp=min(tmp,e[i].v);
        ret+=tmp*dis[T];
        for(int i=fro[T];i;i=fro[e[i].x])e[i].v-=tmp,e[i^1].v+=tmp;
    }
    return ret;
}

int main(){
    scanf("%d",&n);
    int sum=0;
    for(int i=1;i<=n;i++){scanf("%d",&a[i]);sum+=a[i];}
    sum/=n;
    for(int i=1;i<=n;i++)a[i]=a[i]-sum;
    S=0;T=n*2+1;
    for(int i=1;i<=n;i++){
        if(a[i] > 0)
            addedge(S,i,a[i],0);
        else
            addedge(i+n,T,-a[i],0);
        int t;
        t=i>1?i-1:n;
        addedge(i,t,INF,1);addedge(i,n+t,INF,1);
        t=i<n?i+1:1;
        addedge(i,t,INF,1);addedge(i,n+t,INF,1);
    }
    cout<<mcf()<<endl;
}

T20 深海机器人问题

网格上的边连两条,一条容量1费用为其价值,另一条容量INF费用为0,求最大费用最大流。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 500;
const int maxm = 50005;
const int INF  = 0x3f3f3f3f;
struct edge{
    int x,y,next,v,c;
    edge(){}
    edge(int _x,int _y,int _nt,int _v,int _c)
        :x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[maxm];
int  tot=1,head[maxn],fro[maxn],dis[maxn],S,T,a,b,P,Q;
int num[25][25];
bool inq[maxn];
void addedge(int x,int y,int v,int c){
    e[++tot]=edge(x,y,head[x],v,c);head[x]=tot;
    e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;
}
queue<int>q;
bool SPFA(){
    for(int i=S;i<=T;i++)dis[i]=-INF;
    q.push(S);dis[S]=0;inq[S]=1;
    while(!q.empty()){
        int x=q.front();q.pop();inq[x]=0;
        for(int y,c,v,i=head[x];i;i=e[i].next){
            y=e[i].y,v=e[i].v,c=e[i].c;
            if(v && dis[y] < dis[x]+c){
                dis[y]=dis[x]+c;
                fro[y]=i;
                if(!inq[y]){
                    inq[y]=1;q.push(y);
                }
            }
        }
    }
    return dis[T]!=-INF;
}
int mcf(){
    int ret=0;
    while(SPFA()){
        int tmp=INF;
        for(int i=fro[T];i;i=fro[e[i].x])tmp=min(tmp,e[i].v);
        ret+=dis[T]*tmp;
        for(int i=fro[T];i;i=fro[e[i].x])e[i].v-=tmp,e[i^1].v+=tmp;
    }
    return ret;
}
int main(){
    scanf("%d%d",&a,&b);
    scanf("%d%d",&P,&Q);P++;Q++;
    T=0;
    for(int i=1;i<=P;i++)
    for(int j=1;j<=Q;j++)
        num[i][j]=++T;
    S=0;T++;

    for(int i=1;i<=P;i++)
    for(int j=1;j<=Q-1;j++){
        int c;
        scanf("%d",&c);
        int x=num[i][j],y=num[i][j+1];
        addedge(x,y,1,c);
        addedge(x,y,INF,0);
    }
    for(int i=1;i<=Q;i++)
    for(int j=1;j<=P-1;j++){
        int c;
        scanf("%d",&c);
        int x=num[j][i],y=num[j+1][i];
        addedge(x,y,1,c);
        addedge(x,y,INF,0);
    }
    for(int i=1;i<=a;i++){
        int k,x,y;
        scanf("%d%d%d",&k,&x,&y);x++;y++;
        addedge(S,num[x][y],k,0);
    }
    for(int i=1;i<=b;i++){
        int k,x,y;
        scanf("%d%d%d",&k,&x,&y);x++;y++;
        addedge(num[x][y],T,k,0);
    }
    printf("%d\n",mcf());
}

T21 最长K可重区间集问题

将所有出现过的坐标离散化,保存每个区间的长度。从S到最左边点连容量为k费用为0的边,最左边的点到T连容量为K费用为0的边,所有i到i+1连容量为1费用为0的边,每个区间的左右端点所对应的点之间连容量为1费用为区间长度的边,求最大费用最大流。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
const int maxm = 100005;
const int INF  = 0x3f3f3f3f;
#define MADOKA main
struct edge{
    int x,y,next,v,c;
    edge(){}
    edge(int _x,int _y,int _nt,int _v,int _c)
    :x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[maxm];
int tot=1,head[maxn],fro[maxn],dis[maxn],n,k,S,T=0;
bool inq[maxn];
struct node{
    int k,p;
}a[maxn];
int b[maxn<<1],L[maxn];
bool cmp(node x,node y){return x.k<y.k;}
inline int addedge(int x,int y,int v,int c){
    e[++tot]=edge(x,y,head[x],v, c);head[x]=tot;
    e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;
}
queue<int>q;
bool SPFA(){
    for(int i=S;i<=T;i++)dis[i]=-INF;
    dis[S]=0;inq[S]=1;q.push(S);
    while(!q.empty()){
        int x=q.front();q.pop();inq[x]=0;
        for(int y,v,c,i=head[x];i;i=e[i].next){
            y=e[i].y;v=e[i].v;c=e[i].c;
            if( v && dis[y] < dis[x]+c){
                dis[y]=dis[x]+c;
                fro[y]=i;
                if(!inq[y]){
                    inq[y]=1;q.push(y);
                }
            }
        }
    }
    return dis[T]!=-INF;
}
int mcf(){
    int ret=0;
    while(SPFA()){
        int tmp=INF;
        for(int i=fro[T];i;i=fro[e[i].x])tmp=min(tmp,e[i].v);
        ret+=tmp*dis[T];
        for(int i=fro[T];i;i=fro[e[i].x])e[i].v-=tmp,e[i^1].v+=tmp;
    }
    return ret;
}
int MADOKA(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&b[i],&b[i+n]);L[i]=b[i+n]-b[i];
        a[i].k=b[i];
        a[i+n].k=b[i+n];
        a[i].p=i; 
        a[i+n].p=i+n;
    }
    sort(a+1,a+1+n+n,cmp);
    int cnt=0;
    b[a[1].p]=++T;
    for(int i=2;i<=n<<1;i++)
        b[a[i].p]=a[i].k==a[i-1].k?T:++T;
    T++;
    S=0;
    addedge(S,1,k,0);addedge(T-1,T,k,0);
    for(int i=1;i<=T-2;i++)addedge(i,i+1,INF,0);
    for(int i=1;i<=n;i++)addedge(b[i],b[i+n],1,L[i]);
    printf("%d\n",mcf());
}

T24 骑士共存问题

骑士只能从白格跳到黑格,再从黑格跳到白格,所以就是求二分图最大点独立集。二分图最大点独立集等于总点数-匹配数
我的网络流跑太慢了!!!

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
using namespace std;
const int maxn = 205;
const int INF  = 0x3f3f3f3f;
int g[maxn][maxn];
int n,m,S=0,T=0;
const int mx[]={1,2,2,1,-1,-2,-2,-1};
const int my[]={-2,-1,1,2,2,1,-1,-2};
struct edge{
    int x,y,next,v;
    edge(){}
    edge(int _x,int _y,int _nt,int _v)
    :x(_x),y(_y),next(_nt),v(_v){}
}e[maxn*maxn*16];
int head[maxn*maxn],cur[maxn*maxn],tot=1,h[maxn*maxn];
inline void addedge(int x,int y,int v){
    e[++tot]=edge(x,y,head[x],v);head[x]=tot;
    e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
int q[maxn*maxn],l,r;
bool BFS(){
    memset(h,-1,sizeof(h));
    h[S]=0;
    q[l=r=0]=S;
    while(l<=r){
        int x=q[l++];
        if(x==T)return 1;
        for(int v,y,i=head[x];i;i=e[i].next){
            y=e[i].y,v=e[i].v;
            if(v && h[y]==-1){
                h[y]=h[x]+1;
                if(y==T)return 1;
                q[++r]=y;
            }
        }
    }
    return 0;
}
int DFS(int x,int f){
    if(x==T)return f;
    int used=0,tmp;
    for(int v,y,i=cur[x];i;i=e[i].next){
        y=e[i].y;v=e[i].v;
        if(v && h[y]==h[x]+1){
            tmp=DFS(y,min(v,f-used));
            e[i].v-=tmp;e[i^1].v+=tmp;
            used+=tmp;
            if(e[i].v)cur[x]=i;
            if(f==used)return f;
        }
    }
    if(!used)h[x]=-1;
    return used;
}
int maxf(){
    int ret=0;
    while(BFS()){
        for(int i=S;i<=T;i++)cur[i]=head[i];
        ret+=DFS(S,INF);        
    }
    return ret;
}
int main(){
    scanf("%d%d",&n,&m);
    memset(g,0,sizeof(g));
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        g[x][y]=-1;
    }

    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
        if(g[i][j] == 0)g[i][j]=++T;
    S=0;T=n*n+1;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++){
        if(g[i][j]==-1)continue;
        if((i+j) % 2 == 0){
            addedge(S,g[i][j],1);
            for(int k=0;k<8;k++){
                int x=i+mx[k],y=j+my[k];
                if(x<1 || x>n || y<1 || y>n)continue;
                if(g[x][y]==-1)continue;
                addedge(g[i][j],g[x][y],INF);
            }
        }else{
            addedge(g[i][j],T,1);
        }
    }
    printf("%d\n",n*n-m-maxf());
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值