bzoj2127happiness(最小割)

一眼最小割。

一种比较好想的建图方式如下:

连源点表示学文,连汇点表示学理,然后adde(S,id(i,j),a[i][j]),adde(id(i,j),T,b[i][j]);对于相邻座位选择同一科的情况,建立新节点,然后若学文,则新点向T连一条流量为价值的边,然后两名同学向该点连接流量为inf的边。学理也是类似的。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define FOR(x,y) for(int i=1;i<=x;i++)for(int j=1;j<=y;j++)
const int N=50100,M=3e5+7;
using namespace std;
int n,m,T,cnt,tot,ans,hd[N],lv[N],q[N],v[M],nxt[M],w[M];
void adde(int x,int y,int z)
{
    v[++cnt]=y,w[cnt]=z,nxt[cnt]=hd[x],hd[x]=cnt;
    v[++cnt]=x,w[cnt]=0,nxt[cnt]=hd[y],hd[y]=cnt;
}
bool bfs()
{
    int qs=0,qe=1;
    memset(q,0,sizeof q);
    memset(lv,0,sizeof lv);
    q[0]=0,lv[0]=1;
    while(qs<qe)
    {
        int u=q[qs++];
        if(u==T)break;
        for(int i=hd[u];i;i=nxt[i])
        if(w[i]&&!lv[v[i]]){lv[v[i]]=lv[u]+1;q[qe++]=v[i];}
    }
    if(lv[T])return 1;return 0;
}
int dfs(int u,int low)
{
    if(u==T||!low)return low;
    int sum=0;
    for(int i=hd[u];i;i=nxt[i])
    if(w[i]&&lv[v[i]]==lv[u]+1)
    {
        int tmp=dfs(v[i],min(w[i],low));
        sum+=tmp,low-=tmp,w[i]-=tmp,w[i^1]+=tmp;
        if(!low)return sum;
    }
    if(low)lv[u]=-1;
    return sum;
}
int id(int x,int y){return(x-1)*m+y;}
int main()
{
    scanf("%d%d",&n,&m);
    T=5*n*m+1,cnt=1,tot=n*m;
    int x;
    FOR(n,m)scanf("%d",&x),adde(0,id(i,j),x),ans+=x;
    FOR(n,m)scanf("%d",&x),adde(id(i,j),T,x),ans+=x;
    FOR(n-1,m)
    scanf("%d",&x),tot++,adde(0,tot,x),adde(tot,id(i,j),1e9),adde(tot,id(i+1,j),1e9),ans+=x;
    FOR(n-1,m)
    scanf("%d",&x),tot++,adde(tot,T,x),adde(id(i,j),tot,1e9),adde(id(i+1,j),tot,1e9),ans+=x;
    FOR(n,m-1)
    scanf("%d",&x),tot++,adde(0,tot,x),adde(tot,id(i,j),1e9),adde(tot,id(i,j+1),1e9),ans+=x;
    FOR(n,m-1)
    scanf("%d",&x),tot++,adde(tot,T,x),adde(id(i,j),tot,1e9),adde(id(i,j+1),tot,1e9),ans+=x;
    while(bfs())ans-=dfs(0,1e9);
    printf("%d",ans);
}
View Code

 

posted @ 2019-05-31 09:42  hfctf0210  阅读(110)  评论(0编辑  收藏  举报