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\)
使得這兩個點都不在點覆蓋集中,那麼這兩個點一定在獨立集中,同樣與定義矛盾。
至此,我們證明了最大權獨立集等於點權之和減去最小權點覆蓋集,把問題轉化為了上一個模型。