BZOJ 3984 浅谈依赖关系的最大权闭合子图网络流建模


世界真的很大
网络流是个神奇东西
很多涉及到“从总收益中放弃某一部分收益的最大收益”或是“每个单位有2种独立的选择,单位间有依赖关系求最大收益”
都可以理解为“舍弃”最小的收益使满足条件
网络流可以搞搞这类问题
这道题就是这样
还有一道差不多的题,只不过代码量小很多,戳这里
先看下题:
description:

文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠
结过)
 小P所在的班级要进行文理分科。他的班级可以用一个n*m的矩阵进行
描述,每个格子代表一个同学的座位。每位同学必须从文科和理科中选择
一科。同学们在选择科目的时候会获得一个满意值。满意值按如下的方式
得到:
1.如果第i行第秒J的同学选择了文科,则他将获得art[i][j]的满意值,如
  果选择理科,将得到science[i][j]的满意值。
2.如果第i行第J列的同学选择了文科,并且他相邻(两个格子相邻当且
  仅当它们拥有一条相同的边)的同学全部选择了文科,则他会更开
  心,所以会增加same_art[i][j]的满意值。
3.如果第i行第j列的同学选择了理科,并且他相邻的同学全部选择了理
  科,则增加same_science[i]j[]的满意值。
  小P想知道,大家应该如何选择,才能使所有人的满意值之和最大。请
告诉他这个最大值。

input

第一行为两个正整数:n,m
接下来n术m个整数,表示art[i][j];
接下来n术m个整数.表示science[i][j];
接下来n术m个整数,表示same_art[i][j];

output

输出为一个整数,表示最大的满意值之和

在不考虑一起读文或一起读理的情况下,建图方式就十分显然了
源点向所有人连边,边权为此人选文科的收益
所有人向汇点连边,边权为此人选理科的收益
跑一遍最小割,得到的是使得所有人分开到文理科,所需要断开的最小边权和,这是最小割的定义嘛
换句话说就是每个点到源点,汇点这条路上边权最小的边,这里每个点只有两条边,所以就是文科理科收益较小的那一方
用总收益减去最小割便是,这是不考虑依赖的情况
(话说不考虑依赖的话贪心取每个人收益的较大值就好了吧。。)
所以说要是这道题不考虑依赖的话就没有意义了
我们需要把相邻的几个人看成一个整体来考虑
考虑虚拟一个点,源点向其连边,边权为几个人共同选文的权值,再由这个点向这几个人连边,边权为INF,代表断不掉
考虑具体化网络流的过程,某人断掉某边意味着放弃某边的权值意味着放弃某科意味着选择另一科
最小割的目的是使得整个图分成互不相交的两部分,在这道题里就是说使得所有人分成文理科两部分
考虑源点连接的某个虚点,当且仅当虚点所连的所有人与汇点断开连接才能使得虚点完全独立于整个图,且不用断开源点与其的连边。
这种情况就说相邻的几个人只有全部放弃理科选择文科时,才不用断开,放弃全部选文科的额外收益,就是说能获得全部选文的额外收益
如果虚点所连的几个点中有1个或几个点没有断开与汇点的连接,那源点与虚点的连线就必须断开,不然不能使得整个图完全分开
这种情况就是说几个相邻的人只要有一个人不放弃理科,选了理科,那么这几个人就必须放弃选择文科的共同收益
那么建立虚点就能处理一起选文的依赖情况了
对于一起选理科的依赖情况也是建立虚点向汇点连边就行了
代码量可能偏大所以一定要细心
完整代码:

#include<stdio.h>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

const int INF=0x3f3f3f3f; 

struct edge
{
    int v,w,last;
}ed[1000010];

queue <int> state;

int head[1000010],dis[1000010];
int n,m,tot=0,ans=0,num=1,S=0,T;
void add(int u,int v,int w)
{
    num++;
    ed[num].v=v;
    ed[num].w=w;
    ed[num].last=head[u];
    head[u]=num;
}

bool bfs()
{
    memset(dis,-1,sizeof(dis));
    while(!state.empty()) state.pop();
    state.push(S);
    dis[S]=0;
    while(!state.empty())
    {
        int u=state.front();
        state.pop();
        for(int i=head[u];i;i=ed[i].last)
        {
            int v=ed[i].v;
            if(dis[v]==-1&&ed[i].w>0)
            {
                dis[v]=dis[u]+1;
                state.push(v);
            }
        }
    }
    if(dis[T]==-1) return false;
    return true;
}

int dfs(int u,int low)
{
    int a=0;
    if(u==T || low==0) return low;
    for(int i=head[u];i;i=ed[i].last)
    {
        int v=ed[i].v;
        if(ed[i].w>0&&dis[v]==dis[u]+1)
        {
            int tmp=dfs(v,min(low,ed[i].w));
            ed[i].w-=tmp,ed[i^1].w+=tmp;
            a+=tmp;
            low-=tmp;
            if(low==0) return a;
        }
    }
    if(low) dis[u]=-1;
    return a;
}

int main()
{
    scanf("%d%d",&n,&m);
    S=0,T=3*n*m+1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            int w;
            scanf("%d",&w);
            tot+=w;
            add(S,(i-1)*m+j,w);
            add((i-1)*m+j,S,0);
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            int w;
            scanf("%d",&w);
            tot+=w;
            add((i-1)*m+j,T,w);
            add(T,n*m+(i-1)*m+j,w);
            add(n*m+(i-1)*m+j,0);
            add(n*m+(i-1)*m+j,INF);
            add((i-1)*m+j,0);
            if(i>1)
            {
                add(n*m+(i-1)*m+j,(i-2)*m+j,INF);
                add((i-2)*m+j,0);
            }
            if(i<n)
            {
                add(n*m+(i-1)*m+j,i*m+j,INF);
                add(i*m+j,0);
            }
            if(j>1)
            {
                add(n*m+(i-1)*m+j,(i-1)*m+j-1,INF);
                add((i-1)*m+j-1,0);
            }
            if(j<m)
            {
                add(n*m+(i-1)*m+j,(i-1)*m+j+1,INF);
                add((i-1)*m+j+1,0);
            }
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            int w;
            scanf("%d",&w);
            tot+=w;
            add(2*n*m+(i-1)*m+j,2*n*m+(i-1)*m+j,0);
            add((i-1)*m+j,INF);
            add(2*n*m+(i-1)*m+j,0);
            if(i>1)
            {
                add((i-2)*m+j,INF);
                add(2*n*m+(i-1)*m+j,0);
            }
            if(i<n)
            {
                add(i*m+j,0);
            }
            if(j>1)
            {
                add((i-1)*m+j-1,0);
            }
            if(j<m)
            {
                add((i-1)*m+j+1,0);
            }
        }   
    while(bfs())
        ans+=dfs(S,INF);
    printf("%d",tot-ans);
    return 0;
}
/*
Whoso pulleth out this sword from this stone and anvil is duly born King of all England
*/

嗯,就是这样

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结