題目鏈接:
https://www.luogu.com.cn/problem/P3410
思路來源博客:
https://www.luogu.com.cn/blog/user20504/solution-p3410
算法:1:網絡流 最小割 最大權閉合圖 Dinic+當前弧優化
圖解:
思路:
1:我們可以把問題這樣形象的理解一下,對於每一個人來講,有兩個選擇,要麼去拍照,要麼不去,即,要把所有人切割爲兩類
2:然後考慮權值,對於每一個人:如果去,一組人可以掙得拍照費用,但是它們與s的連邊權值其實是0,只有組合在一起纔有收益
3:如果不去,相當於就不用給跑腿錢了,那麼對於去而言,就相當於有了,每個人跑腿費的收益,因此,連接t的點的權值爲跑腿費
4:紅點連每一個點權值爲inf,代表這條邊不會被割斷,即一組都在,拍照纔有報酬
5:sum=拍照總報酬,sum-最大流(最小割)=淨收益
代碼:
#include <bits/stdc++.h>
//Dinic+當前弧優化
using namespace std;
const int maxn=2e2+2,maxm=2e4+4e2+1,inf=0x7fffffff;
int m,n,s,t,tot=1,head[maxn],cur[maxn],dep[maxn],ans,sum,cnt,a;//用上了分層圖,可以用dep判重了
struct edge
{
int to,next,w;
}e[maxm];
void addedge(int x,int y,int w)
{
e[++tot].to=y;e[tot].w=w;e[tot].next=head[x];head[x]=tot;
e[++tot].to=x;e[tot].w=0;e[tot].next=head[y];head[y]=tot;
}
bool bfs()//bool 函數是一個小優化,判斷是否能搜到匯點,如果連匯點都搜不到還dfs幹什麼?
{
memset(dep,0,sizeof dep);//一定要初始化
memcpy(cur,head,sizeof(head));
queue<int>q;
q.push(s);dep[s]=1;
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].next)
{
int y=e[i].to,w=e[i].w;
if(w&&!dep[y])//如果有殘餘流量(沒有的話誰也過不去)並且這個點是第一次到達
{
dep[y]=dep[x]+1;
q.push(y);
}
}
}
return dep[t];//t 的深度不爲0,就是搜到了匯點
}
int dfs(int u,int flow) {
if(u==t) return flow;
int ans=0;
for(int i=cur[u];i&&ans<flow;i=e[i].next) {
cur[u]=i;
int v=e[i].to;
if(e[i].w&&dep[v]==dep[u]+1) {
int x=dfs(v,min(e[i].w,flow-ans));
if(x) e[i].w-=x,e[i^1].w+=x,ans+=x;
}
}
if(ans<flow) dep[u]=-1;//說明這個點已經榨乾
return ans;
}
int main()
{
ios::sync_with_stdio(0);
scanf("%d %d",&m,&n);
s=0,t=n+1,cnt=t;
for(int i=1;i<=m;i++)
{
scanf("%d",&a);sum+=a;
addedge(s,++cnt,a);
while(true)
{
scanf("%d",&a);
if(a==0)break;
addedge(cnt,a,inf);
}
}
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
addedge(i,t,a);
}
while(bfs())ans+=dfs(s,inf);
printf("%d\n",sum-ans);
return 0;
}