bzoj 2127 happiness【最小割+dinic】

参考:https://www.cnblogs.com/chenyushuo/p/5144957.html 不得不说这个建图方法真是非常妙啊
假设S点选理,T点选文,a[i][j]为(i,j)选文收益,b[i][j]为(i,j)选理收益,c[i][j]为同时选文收益,d[i][j]为同时选文收益。
那么对于每个点x=(i+1)*m+j,我们连接

\[c[s,x]=b[i][j] \]

\[c[x,t]=a[i][j] \]

对于有利益相关的x,y两点,连接

\[c[s,x]=d[i][j]/2 \]

\[c[s,y]=d[i][j]/2 \]

\[c[x,t]=c[i][j]/2 \]

\[c[y,t]=c[i][j]/2 \]

\[c[x,y]=c[i][j]/2+d[i][j]/2 \]

\[c[y,x]=c[i][j]/2+d[i][j]/2 \]

建完的图:

然后考虑最小割,下面枚举几种情况:
都选文,即割掉了x选理,y选理和(x,y)都选理:

都选理,即割掉了x选文,y选文和(x,y)都选文:

x选文y选理,即割掉了x选理,y选文,(x,y)都选理/+(x,y)都选理/2+(x,y)都选文/2+(x,y)都选文/2,即,割掉x选理,y选文,(x,y)都选理,(x,y)都选文:

y选文x选理,即割掉了x选文,y选理,(x,y)都选理/+(x,y)都选理/2+(x,y)都选文/2+(x,y)都选文/2,即,割掉x选文,y选理,(x,y)都选理,(x,y)都选文:

对于除以2的操作,为避免下取整的误差,我们选择把所有流量都*2,最后再/2。
$ ans=sum(全部收益)- 最小割 $
p.s.用邻接表建图的时候先把每个点选单科的边连上,再练同时选的收益,否则会重

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=105,E=1000005,inf=1e9,P=500005;
int n,m,a[N][N],b[N][N],c[N][N],d[N][N],s,t,sum,cnt=1,h[P],le[N*N];
struct qwe
{
	int ne,to,va;
}e[E];
int read()
{
	int r=0;
	char p=getchar();
	while(p<'0'||p>'9')
		p=getchar();
	while(p>='0'&&p<='9')
	{
		r=r*10+p-48;
		p=getchar();
	}
	return r;
}
void add(int u,int v,int w)
{
	cnt++;
	e[cnt].ne=h[u];
	e[cnt].to=v;
	e[cnt].va=w;
	h[u]=cnt;
}
bool bfs()
{
	queue<int>q;
    memset(le,0,sizeof(le));
	le[s]=1;
	q.push(s);
    while(!q.empty())
    {
        int u=q.front();//cout<<u<<"AAAAAAAAAAAA"<<endl;
		q.pop();
        for(int i=h[u];i;i=e[i].ne)
            if(e[i].va&&!le[e[i].to])
            {
                le[e[i].to]=le[u]+1;
                q.push(e[i].to);
            }
    }
	// for(int i=0;i<=5;i++)
		// cout<<le[i]<<" ";
    return le[t];
 }
int dfs(int u,int f)
{//cout<<u<<" "<<f<<endl;
    if(u==t)
	{
		//cout<<"!!";
		return f;
	}
    int used=0;
    for(int i=h[u];i;i=e[i].ne)
	{
		//cout<<u<<" "<<e[i].to<<"   "<<e[i].va<<endl;;
        if(e[i].va>0&&le[e[i].to]==le[u]+1)
        {//cout<<"OK"<<endl;
            int w=dfs(e[i].to,min(f-used,e[i].va));
            e[i].va-=w;
            e[i^1].va+=w;
            used+=w;
            if(used==f)
				return f;
        }
	}
    if(!used)
		le[u]=-1;
    return used;
}
int dinic()
{
	int ans=0;
	while(bfs())
		ans+=dfs(s,inf);//,cout<<ans<<endl;
	return ans;
}
int main()
{
	n=read(),m=read();
	s=0,t=n*m+1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			a[i][j]=read(),sum+=a[i][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			b[i][j]=read(),sum+=b[i][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			int x=(i-1)*m+j;
			add(s,x,b[i][j]<<1);
			add(x,s,0);
			add(x,t,a[i][j]<<1);
			add(t,x,0);
		}
	for(int i=1;i<n;i++)
		for(int j=1;j<=m;j++)
			c[i][j]=read(),sum+=c[i][j];
	for(int i=1;i<n;i++)
		for(int j=1;j<=m;j++)
			d[i][j]=read(),sum+=d[i][j];
	for(int i=1;i<n;i++)
		for(int j=1;j<=m;j++)
		{
			int x=(i-1)*m+j,y=i*m+j;
			add(s,x,d[i][j]);
			add(x,s,0);
			add(s,y,d[i][j]);
			add(y,s,0);
			add(x,y,c[i][j]+d[i][j]);
			add(y,x,c[i][j]+d[i][j]);
			add(x,t,c[i][j]);
			add(t,x,0);
			add(y,t,c[i][j]);
			add(t,y,0);
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<m;j++)
			c[i][j]=read(),sum+=c[i][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<m;j++)
			d[i][j]=read(),sum+=d[i][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<m;j++)
		{
			int x=(i-1)*m+j,y=(i-1)*m+j+1;
			add(s,x,d[i][j]);
			add(x,s,0);
			add(s,y,d[i][j]);
			add(y,s,0);
			add(x,y,c[i][j]+d[i][j]);
			add(y,x,c[i][j]+d[i][j]);
			add(x,t,c[i][j]);
			add(t,x,0);
			add(y,t,c[i][j]);
			add(t,y,0);
		}
	printf("%d\n",sum-(dinic()>>1));
	return 0;
}
posted @ 2017-12-28 08:54  lokiii  阅读(119)  评论(0编辑  收藏  举报