这题最重要的是一个模型转换的思想。因为最小割可以代表选择或不选择,那么我们就让每一个最小割的状态分别代表题目所示的每一个状态

  先考虑建图,假设A和B是有关联的两点,那么建如下的图

 

  其中S表示源点,代表文科,T表示汇点,代表理科,A,B是互相关联的两点。这张图的意思是,如果某个点与S相连,代表它选择文科,如果与T相连,代表它选择理科

  那么我们考虑一下,要怎么样才能使全文,全理,一文一理三种情况都能出现呢?

  我们考虑图中边的流量,a=A文+AB文/2,c=B文+AB文/2,b=A理+AB理/2,d=B理+AB理/2,e=f=AB文/2+AB理/2

  因为最小割的割可以代表选择,所以我们可以通过枚举割来枚举选择。那么上图中是不是每一个割都代表了一种选择呢?

  我们设sum=A文+B文+A理+B理+AB文+AB理

  当两人都选文时,我们割去b,d,那么割的大小为A理+B理+AB理,用sum减去割剩下的就是全选文的高兴值

  如果两人都选理,那么我们割去a,c,和上面一个一样,就不多说

  如果两人一文一理怎么办呢?我们假设A文B理,割去a,f,d,那么sum减去割的大小就是A选文和B选理的高兴值

  综上所述,不难发现上图的每一个割都代表了一种选择的状态。那么我们要令高兴值最大,那么割必须最小,只要求出一个最小割就行了

 

 

// luogu-judger-enable-o2
//minamoto
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
inline int read(){
    #define num ch-'0'
    char ch;bool flag=0;int res;
    while(!isdigit(ch=getc()))
    (ch=='-')&&(flag=true);
    for(res=num;isdigit(ch=getc());res=res*10+num);
    (flag)&&(res=-res);
    #undef num
    return res;
}
const int N=10005,M=500005;
int head[N],Next[M],ver[M],edge[M],tot=1;
int dep[N],cur[N],n,m,s,t,mxflow;
int a[105][105],b[105][105],id[105][105],ans;
queue<int> q;
inline void add_edge(int u,int v,int e){
    ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e;
}
inline void ins(int u,int v,int e){
    add_edge(u,v,e),add_edge(v,u,e);
}
inline void insert(int u,int v,int e){
    add_edge(u,v,e),add_edge(v,u,0);
}
bool bfs(){
    memset(dep,-1,sizeof(dep));
    while(!q.empty()) q.pop();
    for(int i=s;i<=t;++i) cur[i]=head[i];
    q.push(s),dep[s]=0;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];i;i=Next[i]){
            int v=ver[i];
            if(dep[v]<0&&edge[i]){
                dep[v]=dep[u]+1,q.push(v);
                if(v==t) return true;
            }
        }
    }
    return false;
}
int dfs(int u,int limit){
    if(u==t||!limit) return limit;
    int flow=0,f;
    for(int i=cur[u];i;i=Next[i]){
        int v=ver[i];cur[u]=i;
        if(dep[v]==dep[u]+1&&(f=dfs(v,min(limit,edge[i])))){
            flow+=f,limit-=f;
            edge[i]-=f,edge[i^1]+=f;
            if(!limit) break;
        }
    }
    if(!flow) dep[u]=-1;
    return flow;
}
void dinic(){
    while(bfs()) mxflow+=dfs(s,inf);
}
void build(){
    int x;s=0,t=n*m+1;
    for(int i=1;i<n;++i)
    for(int j=1;j<=m;++j){
        x=read(),ans+=x;
        a[i][j]+=x,a[i+1][j]+=x;
        ins(id[i][j],id[i+1][j],x);
    }
    for(int i=1;i<n;++i)
    for(int j=1;j<=m;++j){
        x=read(),ans+=x;
        b[i][j]+=x,b[i+1][j]+=x;
        ins(id[i][j],id[i+1][j],x);
    }
    for(int i=1;i<=n;++i)
    for(int j=1;j<m;++j){
        x=read(),ans+=x;
        a[i][j]+=x,a[i][j+1]+=x;
        ins(id[i][j],id[i][j+1],x);
    }
    for(int i=1;i<=n;++i)
    for(int j=1;j<m;++j){
        x=read(),ans+=x;
        b[i][j]+=x,b[i][j+1]+=x;
        ins(id[i][j],id[i][j+1],x);
    }
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j){
        insert(s,id[i][j],a[i][j]);
        insert(id[i][j],t,b[i][j]);
    }
}
int main(){
    //freopen("testdata.in","r",stdin);
    n=read(),m=read();
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    a[i][j]=read(),ans+=a[i][j],a[i][j]<<=1;
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    b[i][j]=read(),ans+=b[i][j],b[i][j]<<=1;
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    id[i][j]=(i-1)*m+j;
    build(),dinic();
    printf("%d\n",ans-(mxflow>>1));
    return 0;
}

 

Logo

集算法之大成!助力oier实现梦想!

更多推荐