【国家集训队2011】happiness 网络最大流

6 篇文章 0 订阅
0 篇文章 0 订阅

【问题描述】

高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友。这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科或者理科,那么他们又将收获一些喜悦值。作为计算机竞赛教练的scp大老板,想知道如何分配可以使得全班的喜悦值总和最大。
【输入格式】

第一行两个正整数n,m。
接下来是六个矩阵
第一个矩阵为n行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学选择文科获得的喜悦值。
第二个矩阵为n行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学选择理科获得的喜悦值。
第三个矩阵为n-1行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i+1行第j列的同学同时选择文科获得的额外喜悦值。
第四个矩阵为n-1行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i+1行第j列的同学同时选择理科获得的额外喜悦值。
第五个矩阵为n行m-1列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i行第j+1列的同学同时选择文科获得的额外喜悦值。
第六个矩阵为n行m-1列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i行第j+1列的同学同时选择理科获得的额外喜悦值。
【输出格式】

输出一个整数,表示喜悦值总和的最大值
【样例输入】

1 2
1 1
100 110
1
1000
【样例输出】

1210
【样例说明】

两人都选理,则获得100+110+1000的喜悦值。
【数据规模和约定】

对于10%以内的数据,n,m<=4
对于30%以内的数据,n,m<=8
对于100%以内的数据,n,m<=100 数据保证答案在2^30以内
对于100%的数据,时间限制为0.5s。

思路:网上主要流传的是解方程的方式,我用了一种求最小割的方式。
首先,考虑最小割就是考虑最小的损失,那么转化一下模型。考虑损失。设置源点为文,汇点为理,那么每个点向源点汇点各连一条边权为喜悦值的边。然后两个点之间有关系就在设置一个点,两个点向这个点连边权为正无穷的边,然后有这个点向源点或汇点连一条边权为两点同时选文或同时选理的喜悦值。然后跑dinic求最小割,如果两个点一个选文一个选理,那么就表示割掉同时选文同时选理两条边,同时选文,则割掉同时选理,反之同理,那么求最小割就表示保证最小的损失。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 1e9
#define MAXN 100005
using namespace std;
queue<int> q;
struct node
{
    int from,to,remain,next;
}e[MAXN*50];
int e_num=-1,point[MAXN];
int deep[MAXN],cur[MAXN];
bool vis[MAXN];
int s,t,ans,n,m;
void add(int from,int to,int remain)
{
    ++e_num;
    e[e_num].from=from;
    e[e_num].to=to;
    e[e_num].remain=remain;
    e[e_num].next=point[from];
    point[from]=e_num;
}
bool bfs(int from,int to)
{
    memset(deep,0x7f,sizeof(deep));
    memset(vis,0,sizeof(vis));
    for (int i=0;i<=t+10;i++)
        cur[i]=point[i];
    deep[from]=0;
    vis[from]=1;
    q.push(from);
    while (!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=false;
        for (int i=point[u];i!=-1;i=e[i].next)
            if (deep[e[i].to]>inf&&e[i].remain)
            {
                deep[e[i].to]=deep[u]+1;
                if (!vis[e[i].to])
                {
                    q.push(e[i].to);
                    vis[e[i].to]=true;
                }
            }
    }
    return deep[to]<inf;
} 

int dfs(int from,int to,int limit)
{
    int f,flow=0;
    if (!limit) return 0;
    if (from==to) return limit;
    for (int i=cur[from];i!=-1;i=e[i].next)
    {
        cur[from]=i;
        if (deep[e[i].to]==deep[from]+1&&(f=dfs(e[i].to,to,min(limit,e[i].remain))))
        {
            flow+=f;
            limit-=f;
            e[i].remain-=f;
            e[i^1].remain+=f;
            if (!limit) break;
        }
    }

    return flow;
}

void dinic()
{
    while (bfs(s,t))
        ans+=dfs(s,t,inf);
}
void addedge(int x,int y,int z)
{
    add(x,y,z);
    add(y,x,0);
}
int main()
{
    freopen("nt2011_happiness.in","r",stdin);
    freopen("nt2011_happiness.out","w",stdout);
    int x,sum=0;
    memset(point,-1,sizeof(point));
    scanf("%d%d",&n,&m);
    s=0;t=n*m*5+100;
    for (int i=1;i<=n;i++)//文 
        for (int j=1;j<=m;j++)
        {
            scanf("%d",&x);
            sum+=x;
            addedge(s,(i-1)*m+j,x);
        }

    for (int i=1;i<=n;i++)  //理 
        for (int j=1;j<=m;j++)
        {
            scanf("%d",&x);
            sum+=x;
            addedge((i-1)*m+j,t,x);
        }
    int po=n*m;
    for (int i=1;i<=n-1;i++) //文 
        for (int j=1;j<=m;j++)
        {
            scanf("%d",&x);
            sum+=x;po++;
            addedge(po,(i-1)*m+j,inf);
            addedge(po,i*m+j,inf);
            addedge(s,po,x);
        }

    for (int i=1;i<=n-1;i++) //理 
        for (int j=1;j<=m;j++)
        {
            scanf("%d",&x);
            sum+=x;po++;
            addedge((i-1)*m+j,po,inf);
            addedge(i*m+j,po,inf);
            addedge(po,t,x);
        }
    for (int i=1;i<=n;i++) //文 
        for (int j=1;j<=m-1;j++)
        {
            scanf("%d",&x);
            sum+=x;po++;
            addedge(po,(i-1)*m+j,inf);
            addedge(po,(i-1)*m+1+j,inf);
            addedge(s,po,x);
        }
    for (int i=1;i<=n;i++) //理 
        for (int j=1;j<=m-1;j++)
        {
            scanf("%d",&x);
            sum+=x;po++;
            addedge((i-1)*m+j,po,inf);
            addedge((i-1)*m+1+j,po,inf);
            addedge(po,t,x);
        }
    dinic();
    printf("%d",sum-ans);  
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值