luogu P1646 [国家集训队]happiness(最小割)

luogu P1646 [国家集训队]happiness(最小割)

题目大意

高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友。这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科或者理科,那么他们又将收获一些喜悦值。

作为计算机竞赛教练的scp大老板,想知道如何分配可以使得全班的喜悦值总和最大。

解题思路

和HDU6598很像,都是对点进行黑白染色,然后点之间有一些根据相对颜色产生的贡献,问如何产生最大的收益。
对此我们可以任何有依赖关系的两个点建一个子图,通过对这幅做一个割表示选择一种染色方法,而把另外三种的贡献割掉。
如此题我们现在对x同学和y同学建一个子图,设x同学选择文科的愉悦指数是 a x a_x ax,选择理科的愉悦指数是 b x b_x bx,y同学选择文科的愉悦指数是 a y a_y ay,选择理科的愉悦指数是 b y b_y by,同选择文科的愉悦指数是aa,同选择理科的愉悦指数是bb,则对子图:
在这里插入图片描述就可以通过构造这四组割
a + e + d = a x + b y + a a + b b b + c + e = a y + b x + a a + b b a + b = a x + a y + a a c + d = b x + b y + b b \begin{aligned} a+e+d&=a_x+b_y+aa+bb\\ b+c+e&=a_y+b_x+aa+bb\\ a+b&=a_x+a_y+aa\\ c+d&=b_x+b_y+bb \end{aligned} a+e+db+c+ea+bc+d=ax+by+aa+bb=ay+bx+aa+bb=ax+ay+aa=bx+by+bb
最后可以解方程得到
a = a x + a a 2 b = a y + a a 2 c = b x + b b 2 d = b y + b b 2 \begin{aligned}\\ a&=a_x+\frac{aa}{2}\\ b&=a_y+\frac{aa}{2}\\ c&=b_x+\frac{bb}{2}\\ d&=b_y+\frac{bb}{2} \end{aligned} abcd=ax+2aa=ay+2aa=bx+2bb=by+2bb
而对于每个点其自身的权值只需要加一次就可以了,而和其他点之间的关联贡献则所有的边都必须加上。据此进行建图,跑出最小割,总的所有的贡献和减去最小割就是答案

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=10005;
const int maxm=2e5+5;
const int inf=0x3f3f3f3f;
struct Edge{
    int to,nxt,cap,flow;
}edge[maxm];
int tol;
int head[maxn];
void init(){
    tol=2;
    memset(head,-1,sizeof(head));
}
void AddEdge(int u,int v,int w,int rw=0){
    edge[tol].to=v;edge[tol].cap=w;edge[tol].flow=0;
    edge[tol].nxt=head[u];head[u]=tol++;
    edge[tol].to=u;edge[tol].cap=rw;edge[tol].flow=0;
    edge[tol].nxt=head[v];head[v]=tol++;
}
int Q[maxn];
int dep[maxn],cur[maxn],sta[maxn];
bool bfs(int s,int t,int n){
    int front=0,tail=0;
    memset(dep,-1,sizeof(dep[0])*(n+1));
    dep[s]=0;
    Q[tail++]=s;
    while(front<tail){
        int u=Q[front++];
        for(int i=head[u];i!=-1;i=edge[i].nxt){
            int v=edge[i].to;
            if(edge[i].cap>edge[i].flow&&dep[v]==-1){
                dep[v]=dep[u]+1;
                if(v==t) return true;
                Q[tail++]=v;
            }
        }
    }
    return false;
}
int dinic(int s,int t,int n){
    int maxflow=0;
    while(bfs(s,t,n)){
        for(int i=0;i<n;i++) cur[i]=head[i];
        int u=s,tail=0;
        while(cur[s]!=-1){
            if(u==t){
                int tp=inf;
                for(int i=tail-1;i>=0;i--)
                {
                    tp=min(tp,edge[sta[i]].cap-edge[sta[i]].flow);
                }
                maxflow+=tp;
                for(int i=tail-1;i>=0;i--){
                    edge[sta[i]].flow+=tp;
                    edge[sta[i]^1].flow-=tp;
                    if(edge[sta[i]].cap-edge[sta[i]].flow==0) tail=i;
                }
                u=edge[sta[tail]^1].to;
            }
            else if(cur[u]!=-1&&edge[cur[u]].cap>edge[cur[u]].flow&&dep[u]+1==dep[edge[cur[u]].to]){
                sta[tail++]=cur[u];
                u=edge[cur[u]].to;
            }
            else{
                while(u!=s&&cur[u]==-1) u=edge[sta[--tail]^1].to;
                cur[u] = edge [cur[u]].nxt;
            }
        }
    }
    return maxflow;
}
const int size=105;
int n,m;
int a[size][size],b[size][size];
int aay[size][size],bby[size][size];
int aax[size][size],bbx[size][size];
inline int Hash(int x,int y){return (x-1)*m+y;}
int main()
{
	//freopen("p1646.in","r",stdin);
	scanf("%d%d",&n,&m);
	int ans=0;
	init();
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%d",&a[i][j]);
			a[i][j]*=2;
			ans+=a[i][j];
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%d",&b[i][j]);
			b[i][j]*=2;
			ans+=b[i][j];
		}
	}
	for(int i=1;i<n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%d",&aay[i][j]);
			aay[i][j]*=2;
			ans+=aay[i][j];
		}
	}
	for(int i=1;i<n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%d",&bby[i][j]);
			bby[i][j]*=2;
			ans+=bby[i][j];
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<m;j++)
		{
			scanf("%d",&aax[i][j]);
			aax[i][j]*=2;
			ans+=aax[i][j];
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<m;j++)
		{
			scanf("%d",&bbx[i][j]);
			bbx[i][j]*=2;
			ans+=bbx[i][j];
		}
	}
	int s=0,t=n*m+1;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			int aa=0,bb=0;
			if(i-1>=1) aa+=aay[i-1][j],bb+=bby[i-1][j],AddEdge(Hash(i,j),Hash(i-1,j),(aay[i-1][j]+bby[i-1][j])/2);
			if(i<n) aa+=aay[i][j],bb+=bby[i][j],AddEdge(Hash(i,j),Hash(i+1,j),(aay[i][j]+bby[i][j])/2);
			if(j-1>=1) aa+=aax[i][j-1],bb+=bbx[i][j-1],AddEdge(Hash(i,j),Hash(i,j-1),(aax[i][j-1]+bbx[i][j-1])/2);
			if(j<m) aa+=aax[i][j],bb+=bbx[i][j],AddEdge(Hash(i,j),Hash(i,j+1),(aax[i][j]+bbx[i][j])/2);
			aa/=2;bb/=2;
			aa+=a[i][j],bb+=b[i][j];
			AddEdge(s,Hash(i,j),aa);
			AddEdge(Hash(i,j),t,bb);
		}
	}
	printf("%d\n",(ans-dinic(s,t,t+1))/2);		    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值