世界真的很大
网络流是个神奇东西
很多涉及到“从总收益中放弃某一部分收益的最大收益”或是“每个单位有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
*/
嗯,就是这样