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,(i-1)*m+j,0);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int w;
scanf("%d",&w);
tot+=w;
add(S,n*m+(i-1)*m+j,w);
add(n*m+(i-1)*m+j,S,0);
add(n*m+(i-1)*m+j,(i-1)*m+j,INF);
add((i-1)*m+j,n*m+(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,n*m+(i-1)*m+j,0);
}
if(i<n)
{
add(n*m+(i-1)*m+j,i*m+j,INF);
add(i*m+j,n*m+(i-1)*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,n*m+(i-1)*m+j,0);
}
if(j<m)
{
add(n*m+(i-1)*m+j,(i-1)*m+j+1,INF);
add((i-1)*m+j+1,n*m+(i-1)*m+j,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,T,w);
add(T,2*n*m+(i-1)*m+j,0);
add((i-1)*m+j,2*n*m+(i-1)*m+j,INF);
add(2*n*m+(i-1)*m+j,(i-1)*m+j,0);
if(i>1)
{
add((i-2)*m+j,2*n*m+(i-1)*m+j,INF);
add(2*n*m+(i-1)*m+j,(i-2)*m+j,0);
}
if(i<n)
{
add(i*m+j,2*n*m+(i-1)*m+j,INF);
add(2*n*m+(i-1)*m+j,i*m+j,0);
}
if(j>1)
{
add((i-1)*m+j-1,2*n*m+(i-1)*m+j,INF);
add(2*n*m+(i-1)*m+j,(i-1)*m+j-1,0);
}
if(j<m)
{
add((i-1)*m+j+1,2*n*m+(i-1)*m+j,INF);
add(2*n*m+(i-1)*m+j,(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
*/
嗯,就是這樣