2127: happiness 最小割

本文介绍了一种利用最小割算法解决团队成员配置问题的方法,通过构建特定的图模型,解决成员选择文理科以最大化团队整体收益的问题。文章详细解释了如何通过调整边权值来确保算法正确处理相邻成员选择相同科目的额外加分。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

好题。
去年夏令营的时候似乎davidwang神犇讲过,我一定是睡过去了qwq。。
时隔半年来做一下这道题。
首先我们不考虑相邻的相同有加成的情况,那么每个人只对应文和理两种情况,如果用最小割(不要和我说贪心)的思路来解决,也就是将所有人分到不同的集合,一个为S集,假设代表学文科,另一个为T集,假设代表学理科。Ai代表i选文的喜悦值,Bi代表i选理的喜悦值。那么这就成了一道noip普(xiao)及(xue)组的题,我们可以如下建图:

四种合法的割集{A1,A2} {A1,B2} {B1,B2} {A2,B1}分别代表了四种合法的方案。拿到本题中来说,{A1,B2} {A2,B1}也代表了1和2的选择不同。但是现在的问题是,{A1,A2} {B1,B2}这两种割集并没有对应到同时选文或同时选理的喜悦值。
我们拿第一种情况来考虑,如果选了{A1,A2},那么我们应当取得的是B1,B2和1、2共同选理的喜悦值,应当舍弃的是A1,A2和1、2同时
选文的喜悦值,因为是由1、2共同决定的,我们不妨改变一下边的权值。这里用C[i][j]代表i和j共同选文的喜悦值,D[i][j]代表i和j共同选理的喜悦值。那么我们可以如下建图:

这样刚才的问题就解决了,{A1,A2} {B1,B2}这两种割集有了合法的意义。
我们再回头看一下刚才的{A1,B2} {A2,B1},发现我们多获得了一些喜悦值,那就是C[1][2]/2+D[1][2]/2。为了减去这一部分,我们可以再连一条边,使这条边的权值为C[1][2]/2+D[1][2]/2,并且要保证在这种方案下这条边一定能被割掉。那么我们可以如下建图:

这样这个问题就得到了解决,并且发现刚才那种情况也不会受到影响。所以这种建图是合法的。
需要注意的是除以二可能会出问题,可以统一乘二。

今天手感不错?写完编译样例AC异常顺畅QAQ。。

#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 2147483647
using namespace std;
int n,m,cnt=1,sum,S,T;
int head[10005],dis[10005],cur[10005],q[10005];
int next[1200005],list[1200005],key[1200005];
int a[105][105],b[105][105];
inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
inline void insert(int x,int y,int z)
{
    next[++cnt]=head[x];
    head[x]=cnt;
    list[cnt]=y;
    key[cnt]=z;
}
inline int c(int i,int j)
{
    return (i-1)*m+j;
}
inline void pre()
{
    n=read(); m=read(); S=0; T=n*m+1;
    int x;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            a[i][j]=read(),sum+=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(),sum+=b[i][j],b[i][j]<<=1;
    for (int i=1;i<n;i++)
        for (int j=1;j<=m;j++)
            x=read(),sum+=x,a[i][j]+=x,a[i+1][j]+=x,insert(c(i,j),c(i+1,j),x),insert(c(i+1,j),c(i,j),x);
    for (int i=1;i<n;i++)
        for (int j=1;j<=m;j++)
            x=read(),sum+=x,b[i][j]+=x,b[i+1][j]+=x,insert(c(i,j),c(i+1,j),x),insert(c(i+1,j),c(i,j),x);
    for (int i=1;i<=n;i++)
        for (int j=1;j<m;j++)
            x=read(),sum+=x,a[i][j]+=x,a[i][j+1]+=x,insert(c(i,j),c(i,j+1),x),insert(c(i,j+1),c(i,j),x);
    for (int i=1;i<=n;i++)
        for (int j=1;j<m;j++)
            x=read(),sum+=x,b[i][j]+=x,b[i][j+1]+=x,insert(c(i,j),c(i,j+1),x),insert(c(i,j+1),c(i,j),x);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            insert(S,c(i,j),a[i][j]),insert(c(i,j),S,0),insert(c(i,j),T,b[i][j]),insert(T,c(i,j),0);
}
inline bool BFS()
{
    memset(dis,-1,sizeof(dis));
    dis[S]=1; q[1]=S; 
    int t=0,w=1,x;
    while (t<w)
    {
        x=q[++t];
        for (int i=head[x];i;i=next[i])
            if (key[i]&&dis[list[i]]==-1)
                dis[list[i]]=dis[x]+1,q[++w]=list[i];
    }
    return dis[T]!=-1;
}
int find(int x,int flow)
{
    if (x==T) return flow;
    int w,used=0;
    for (int i=cur[x];i;i=next[i])
        if (key[i]&&dis[list[i]]==dis[x]+1)
        {
            w=find(list[i],min(flow-used,key[i]));
            key[i]-=w; key[i^1]+=w; used+=w;
            if (key[i]) cur[x]=i;
            if (used==flow) return flow;
        }
    if (!used) dis[x]=-1;
    return used;
}
inline int dinic()
{
    int tmp=0;
    while (BFS()) 
    {
        for (int i=S;i<=T;i++) cur[i]=head[i];
        tmp+=find(S,inf);
    }
    return tmp;
}
int main()
{
    pre(); 
    printf("%d\n",sum-(dinic()>>1));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值