洛谷 P3410 拍照 網絡流 最小割 最大權閉合圖 Dinic+當前弧優化

題目鏈接:

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;
}

 

發佈了151 篇原創文章 · 獲贊 68 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章