1.1.3.3 最小割之最小權覆蓋集、最大權獨立集

Dai_Fu發表於2024-03-09

1.1.3.3 最小割之最小權覆蓋集、最大權獨立集

最小權點覆蓋

給定一個無向圖點帶權。選擇某些點,使得點所連邊能夠包含整張圖的所有點,則這個點集叫做點覆蓋集;點權之和最小的點覆蓋集就叫最小權點覆蓋集

最小權點覆蓋問題對於一般圖是 NP-完全 問題,即不存在多項式解法的問題,已經證明只能爆搜求解

二分圖的最小權點覆蓋集

要求:點權非負

\(最大匹配數=最小點覆蓋數=n-最大獨立集數\)

源點連一邊,匯點連另一邊,邊容量等於權值;中間點連邊容量為正無窮,這個問題就可以類比成最小割模型了。

接下來證明可行解與簡單割的一一對應關係。首先可以發現,簡單割由於不包含正無窮容量邊,所以簡單割一定可以對應構造得到點覆蓋集;反之,我們可以透過點覆蓋集發現某些邊是否可走,那麼按照此方式進行 dfs,就會把整個點集分為兩部分:可到達為 S
,不可到達為 T。顯然這樣的分配方式滿足簡單割。綜上,可行解與簡單割一一對應,簡單割的容量就等於點覆蓋集的點權之和。

相關概念

2325. 有向圖破壞

這個題最關鍵的是拆點,對於每一條邊u,v,要麼選擇u-,要麼v+,那麼對於這個圖而言拆點後,每個點分為u+,u-.觀察得出,這個圖永遠是個二分圖(按照只有出邊還是隻有入邊分)。所以自然會聯想到用最大流求二分圖匹配的演算法。那麼這裡顯然不是最大流,仔細想想,會發現和最大流的對偶問題——最小割有關。
咋建圖呢?可新建一個S,T. S連線所有u- 邊權是w- T連線所有u+ 邊權是w+ 在原圖的每條邊上連上正無窮大小邊(目的是不讓這些邊被選為最小割中的邊)。然後用dinic跑最小割就行(觀察發現所有的割邊都是S,u or u,T ,而這些邊的權值已經被賦值成了w- or w+ ,而原圖中每一條邊都會被算在最小割裡面,實現了原問題與最小割的一一對應)

然後還需要求出操作方案,而原問題的每個操作都對應到二分圖中的每個點,所以求操作方案其實等價於求最小權的點覆蓋集。這裡結合證明過程中根據最小割構造點覆蓋集的方法。先從源點開始往下搜,所有能走到的點在 S 集合,所有走不到的點在 T 集合,然後列舉所有正向邊,找出所有割邊(起點在 S,終點在 T 的邊),然後將所有割邊中除了源點、匯點的點都找出來,就是最小權的點覆蓋集。

#include<bits/stdc++.h>
using namespace std;
const int MX_N=510,MX_M=50100;
struct node{
    int next,to,w;
}edge[MX_M<<1];
int head[MX_N]={0},edge_cnt=0;
inline void Add(int x,int y,int w){
    node &i=edge[edge_cnt];
    i.next=head[x],i.to=y,i.w=w;
    head[x]=edge_cnt++;
}
inline void add(int x,int y,int w){
    Add(x,y,w),Add(y,x,0);
}
int s=0,t=MX_N-1;
int cur[MX_N]={0},dist[MX_N]={0};
bool bfs(){
    for(int i=0;i<MX_N;i++)  dist[i]=-1,cur[i]=head[i];
    queue<int >qu;
    qu.push(s);dist[s]=0;
    while(!qu.empty()){
        int now=qu.front();qu.pop();
        
        for(int i=head[now];~i;i=edge[i].next){
            int to=edge[i].to,w=edge[i].w;
            if(dist[to]==-1&&w){
                dist[to]=dist[now]+1;
                qu.push(to);
            }
        }
    }
    return dist[t]!=-1;
}
int dfs(int now,int flow){
    if(now==t)  return flow;
    int left=flow;
    for(int &i=cur[now];~i;i=edge[i].next){
        int to=edge[i].to,w=edge[i].w;
        if(dist[to]==dist[now]+1&&w){
            int cur_flow=dfs(to,min(w,left));
            left-=cur_flow;
            edge[i].w-=cur_flow;
            edge[i^1].w+=cur_flow;
            if(left==0)  break;
        }
    }
    if(left==flow)  dist[now]=-1;
    return flow-left;
}
int dinic(){
    int sum=0;
    while(bfs())  sum+=dfs(s,0x3f3f3f3f);
    return sum;
}
bool st[MX_N]={0};
void dfs1(int now){
    st[now]=1;
    for(int i=head[now];~i;i=edge[i].next){
        int to=edge[i].to;
        if(st[to]==0&&edge[i].w)  dfs1(to);
    }
}
signed main(){
    memset(head,-1,sizeof(head));
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        int x;scanf("%d",&x);//+
        add(i+n,t,x);
    }
    for(int i=1;i<=n;i++){
        int x;scanf("%d",&x);//-
        add(s,i,x);
    }
    for(int i=1;i<=m;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v+n,0x3f3f3f3f);
    }
    printf("%d\n",dinic());
    dfs1(s);
    int k=0;
    for(int i=0;i<edge_cnt;i+=2){
        int u=edge[i^1].to,v=edge[i].to;
        if(st[u]&&!st[v])  k++;
    }
    printf("%d\n",k);
    for(int i=0;i<edge_cnt;i+=2){
        int u=edge[i^1].to,v=edge[i].to;
        if(st[u]&&!st[v]){
            if(v==t){
                printf("%d +\n",u-n);
            }
            else if(u==s){
                printf("%d -\n",v);
            }
        }
    }
    return 0;
}

最大權獨立集

對於一個無向圖,選取某些點使得兩點之間沒有直接連邊,那麼這個點集就是一個獨立集。最大權獨立集就是點權之和最大的獨立集。

最大權獨立集問題同樣是一個 NP-完全問題,所以還是隻能在二分圖上考慮。

二分圖的最大權獨立集

要求:點權非負

\(最大獨立集=n-最小覆蓋集\)

\(最大權獨立集=所有點的總權值-最小權點覆蓋\)

證明一下最大權獨立集與最小權覆蓋集之間的對應關係。先看點覆蓋集對應獨立集。運用反證法,假設點覆蓋集的補集不是獨立集,那麼在獨立集中一定存在兩點 \(u\),\(v\) 使得它們之間有連邊,反之點覆蓋集就不會包含這兩個點,與定義矛盾。再看獨立集對應點覆蓋集,同樣使用反證法,假設獨立集的補集不是點覆蓋集,那麼一定存在 \(u\),\(v\)

使得這兩個點都不在點覆蓋集中,那麼這兩個點一定在獨立集中,同樣與定義矛盾。

至此,我們證明了最大權獨立集等於點權之和減去最小權點覆蓋集,把問題轉化為了上一個模型。

相關文章