擬陣
(latest updated on 2020-08-10)
大量基礎定義警告,參考了wiki和2018論文《淺談擬陣的一些拓展及其應用》,如果想看大段詳細證明請移步論文
擬陣的概念比較抽象,有多種定義方法,結合這些定義方法可以更具體地了解擬陣的基礎性質
前言
很多問題可以轉化為擬陣,但是並不是所有問題都可以通過簡單的擬陣操作得到答案
在具體問題中,很多時候有着更優的算法解決擬陣運算無法解決的操作
但是對於一個奇怪的問題,如果轉化為類似擬陣的操作后,就有很多性質可以拿過來套
擬陣的應用,更多還是用 諸多的性質 把復雜,抽象問題向更簡單的方向轉化(便於亂搞)
也可以便於簡化問題的證明,所以這個東西了解一下也差不多了
(不會有人喪心病狂到專門出一個擬陣交的題吧)
符號及約定
\(|S|\)集合大小
\(S-T\),刪除\(S\)中在\(T\)中的元素
\(a\Rightarrow b\)若\(a\)則\(b\)
\(a\Leftrightarrow b\),\(a,b\)等價
\(a\in b\)元素\(a\)是集合\(b\)中的一個元素
\(a\sube b\),集合\(a\)是集合\(b\)的子集
\(\exists\) 存在
\(\forall\) 任意
冪集
一個集合\(S\)的所有\(2^{|S|}\)個子集構成的集合是\(S\)的冪即\(P(S)\)或者\(2^S\)
集族
給定集合\(S\) 的一些子集構成的類\(F\)叫做\(S\)的子集族(或稱S 上的集合族),\(F \sube 2^S\)
用獨立集定義擬陣(似乎是最直觀的定義)
一個二元組\(M=(E,I)\),其中\(E\)是基礎集,\(I\)是\(E\)的一些子集構成的集族(即\(I\sube 2^S\)),稱之為獨立集,在獨立集中的子集稱之為獨立的
擬陣可以用獨立集\(I\)定義,則\(I\)需滿足性質:
1.空集:有\(\emptyset \in I\),所以有\(I\ne \emptyset\)
2.遺傳性:若\(A\sube B,B\in I\),則$ A\in I$
3.擴充性:若\(\exists A,B\in I,|A|>|B|\),則\(\exists i\in A,(B\cup \{i\}) \in I\)
例子:對於\(S=\{1,2,3\}\),\(\{\emptyset \},\{\emptyset,\{1\}\}\)是合法的獨立集,但\(\{\emptyset,\{1\},\{2\},\{3\},\{2,3\}\}\)不是,因為它不滿足擴充性
\(\{\{1,2\},\{2,3\},\{1\},\{2\},\{3\},\{\emptyset \}\}\)也是合法的獨立集
對於\(S=\{1,2,3,4,5\}\),\(I=\{\{1,2,3\},\{3,4,5\},\{1,2\},\{2,3\},\{1,3\},\{3,4\},\{3,5\},\{4,5\},\{1\},\{2\},\{3\},\{5\},\{\emptyset\}\}\)不是合法的獨立集,因為它不滿足擴充性(\(A=\{1,2,3\},B=\{3,4\}\)時)
用基底和基定義擬陣(似乎是最簡潔的描述)
基底:\(E\)的一個獨立的極大子集稱為其的一個基底,獨立的極大子集即其加入任意元素得到的子集不獨立
基:\(E\)的基\(B\)為其所有基底構成的集合
擬陣可以用基\(B\)定義,則\(B\)需滿足性質:
1.非空:\(B\ne \emptyset\),最小的\(B=\{\emptyset\}\)
2.交換公理:對於兩個基底\(a,b\),若用\(b\)中\(a\)沒有的元素換掉一個\(a\)中原先的元素,得到的集合依然是基底
推論:基底等大,即\(\forall a\in B,b\in B,|a|=|b|\)(否則就不滿足擴充性)
例如:若\(\{1,2\},\{1,3\}\)是基底,則\(\{2,3\}\)也是基底(否則不滿足擴充性)
可以得到擬陣的等價定義,且\(I=\bigcup _{T\in B} 2^T\)
用環路集定義擬陣
環路:\(S\)的一個子集是環路,則這個子集是一個極小的非獨立集,即去掉任意一個元素都會稱為獨立集
所有環路構成的集合稱為環路集\(C\),如對於\(E=\{1,2\},I=\{\emptyset,\{1\}\}\),環路集為\(\{\{2\}\}\)
擬陣可以用環路集\(C\)定義,則\(C\)需滿足性質:
1.\(C\)可以為空(此時\(I=P(S)\)),且\(\emptyset \not \in C\)
2.環路互相之間不是真子集,即\(\exists a\in C,b\in C,a\sube b\Rightarrow a=b\) (否則不滿足遺傳性)
3.若\(\exists a\in C,b\in C,a\ne b\)以及一個元素\(i\in a\cap b\),則\(a\cup b-\{i\}\)不是獨立集
推論:\(A\sube I \Leftrightarrow \nexists B\in C,B\sube A\)
環路不一定等大
環路和基底的一些關系
1.環路和基底之間不能通過加減一個元素轉化
2.基底加上一個元素得到的非獨立集恰好包含一個環路
擬陣的秩
擬陣的秩:擬陣的任意一個基底的元素個數是其秩\(r\)
為了同下文的秩函數相對應,也可說\(\begin{aligned}r=\max_{R\in I}\{|R|\}\end{aligned}\),即最大的獨立集大小
用秩函數定義擬陣
對於元素集\(S\)
若可以定義一個在\(2^S\)上的秩函數\(r(T)\),滿足以下性質:
1.大小有界:\(r(T)\in[0,|T|]\)
2.大小傳遞性:\(A\sube B\Rightarrow r(A)\le r(B)\)
3.次模性:\(r(A\cup B)+r(A\cap B)\le r(A)+r(B)\)
那么可以用這樣的一個秩函數定義一個擬陣\(M=(S,r)\),此時\(r(T)\)為\(2^T\)中極大的獨立集大小,且擬陣的獨立集就是\(I=\{T|T\sube S,r(T)=|T|\}\)
例子
均勻擬陣:\(U_n^k=(S,I),|S|=n,I=\{T|T\sube S,|T|\leq k\}\)
圖擬陣:
對於無向圖\(G=(V,E)\),它的生成擬陣是\(M=(E,\{T|T\sube E,T無環\})\)
它的最大獨立子集大小為\(G\)的最大生成森林邊數,每個最大生成森林都是基底
匹配擬陣:
對於無向圖\(G=(V,E)\),它的匹配擬陣是\(M=(V,\{T|T\sube V,存在一個邊匹配覆蓋T\})\)
它的最大的獨立子集大小為k最大匹配數,每個最優匹配的方案都是基底
異或線性基:
對於非負整數可重集合\(S\),擬陣是\(M=(S,\{T|T\sube S,T中的元素任意異或不會得到0\})\)
(這是向量空間的線性基問題的一種)
注意分清楚 定義需要滿足的條件 和 通過條件推導得到的性質 的區別 ,上面幾種擬陣定義是等價的
一些應用
求最大權值獨立子集
對於擬陣\(M=(S,I)\),給每個\(S\)中每個元素一個非負權值,定義一個集合的權值為所有元素權值和,要求最大權值的獨立子集
這是一個非常簡單的問題,直接從大到小能加入就加入即可,設最終選出的集合為\(P\)
證明:
1.\(P\in B\),否則可以再加入元素
2.假設存在更優解\(Q\in B\),根據基底交換公理,一定可以用\(P\)中一個權值更大的元素換掉\(Q\)中一個元素,所以\(Q\)不是合法集合
在連通圖擬陣上使用該算法,就是\(\text{Kruskal}\)最小生成數算法
在異或線性基上使用該算法,可以求得最大權值線性基
擬陣交
對於同基礎集的擬陣\(M_1=(S,I_1),M_2=(S,I_2)\),它們的交是獨立集的交
但是它們的交不一定是擬陣
求解最大交:最小最大定理:
\(\begin{aligned} \max_{A\in (I_1\cup I_2)}\{|T|\}=\min_{R\in S}\{r_1(R)+r_2(S-R)\}\end{aligned}\)
這個東西的證明分為兩步:
1.證明\(\max\leq \min\)
\(|T|=|T\cap R|+|T\cap (S-R)|\leq r_1(R)+r_2(S-R)\)
2.介紹找到兩個最值的算法
這個算法的中心是,從空集開始擴展\(T\),並且相應找到對應的\(R\)使得\(|T|=r_1(R)+r_2(S-R)\),此時答案已經充分了
求解擬陣交的算法基於一個構造的圖
對於當前的答案\(T\),構造一個有向二分圖,兩側點集分別為\(T,S-T\)
對於分別在兩側的點\(x,y\),
存在\(x\rightarrow y\)的邊: 當\(T\)中把\(x\)換成\(y\)之后是\(M_1\)的獨立子集
存在\(y\rightarrow x\)的邊: 當\(T\)中把\(y\)換成\(x\)之后是\(M_2\)的獨立子集
設對於\(I_1,I_2\)可行的增廣元素集合為$X_1,X_2 $(即加入元素后依然獨立的集合)
每次的增廣過程可以描述為:
(1.如果\(X_1\cap X_2\ne \emptyset\),直接都加入\(T\))
2.構圖,找到一條從\(X_1\)的點出發,到達\(X_2\)的的最短的路徑\(P\)(廣搜即可),將\(I\)變為\(I\bigoplus P\) (這里原文是對稱差,但是異或大家都懂哈)
當不存在增廣時,找到了最大的\(T\),此時對應合法的\(R\)為\(\{e\in S|在圖中存在e到達X_2的路徑\}\)
由於每次增廣至少增加一個元素,該算法的復雜度上限為\(O(r|S|^2)\),其中\(r\)為擬陣的秩,\(|S|^2\)為邊數
帶權擬陣交
每個元素帶權
在增廣時,圖上每個點加上點權(加入為正,刪除為負),每次求出點權最短路進行增廣即可
擬陣交的應用
二分圖匹配問題
二分圖匹配匈牙利算法 是 求解擬陣交的問題 的一種特殊情況
對於二分圖\(G=(V_1,V_2,E)\),構造
\(M_1=(E,I=\{T\sube E|T中的邊在V_1上沒有公共點\})\)
\(M_2=(E,I=\{T\sube E|T中的邊在V_2上沒有公共點\})\)
答案就是\(M_1,M_2\)交的最大值,匈牙利算法增廣的過程是依次考慮\(X_1\)中的元素進行增廣
(帶權的二分圖匹配問題,實際也是可以用擬陣解決的,但是好像\(\text{KM}\)還是最棒的)
擬陣交還可以解決一些看起來很抽象的 帶有兩個限制的 (可能帶權) 的問題,比如論文里下面的這個例子
(Colorful Tree): 對於一個無向圖,每條邊給定一個顏色和一個權值,求顏色不能重復的最大生成樹
類似這樣的問題可以轉化為擬陣交問題,但是這個東西的局限性實在太大,也沒有人敢動
update:
有生之年竟然用上了這個東西?
**模擬賽有人搞了一個題:
對於一個無向圖\(G=(V,E),E=(u,v,w)\),其中\(w\)為每條邊的顏色
要求選出一個最大的邊集,滿足:
1.每種顏色\(i\)選出的邊不超過\(c_i\)條
2.選出的邊不構成簡單環
然后寫了一次不太正規的擬陣交模板
令\(M1\)為個數的擬陣,\(M2\)為生成樹擬陣
解釋在代碼里
int n,m,k;
int A[N],I[N];
// A為顏色個數的限制
int U[N],V[N],W[N],F[N];
// U,V,W為邊
// F 為並查集
int Find(int x){ return F[x]==x?x:F[x]=Find(F[x]); }
int X[N],Y[N],P[N];
// 處理生成樹的情況
vector <int> G[N];
int fa[N],fe[N],dep[N];
void dfs(int u,int f){
dep[u]=dep[fa[u]=f]+1;
for(int i:G[u]) {
int v=U[i]==u?V[i]:U[i];
if(v==f) continue;
fe[v]=i,dfs(v,u);
}
}
int main(){
freopen("forget.in","r",stdin),freopen("forget.out","w",stdout);
n=rd(),m=rd(),k=rd();
rep(i,1,k) A[i]=rd();
rep(i,1,m) U[i]=rd(),V[i]=rd(),W[i]=rd();
rep(i,1,n) F[i]=i;
while(1) {
int f=0;
// X, Y分別為 M1,M2的可拓展集合
// I 記錄已經在選邊方案里的邊
rep(i,1,m) if(!I[i]) {
X[i]=A[W[i]]>0,Y[i]=Find(U[i])!=Find(V[i]);
// 如果X,Y有交,就直接加入集合
if(X[i] && Y[i]) {
A[W[i]]--,F[Find(U[i])]=Find(V[i]);
I[i]=1,f=i;
}
} else X[i]=Y[i]=0;
if(f) continue;
static queue <int> que;
while(!que.empty()) que.pop();
// 預處理當前的生成樹,便於處理M2的限制
rep(i,1,n) G[i].clear();
rep(i,1,m) if(I[i]) G[U[i]].pb(i),G[V[i]].pb(i);
rep(i,1,n) dep[i]=0;
rep(i,1,n) if(!dep[i]) dfs(i,0);
// 從X中的某一個點出發
rep(i,1,m) if(X[i]) que.push(i),P[i]=-1;
else P[i]=0;
f=0;
// 廣搜找交替 替換路徑
while(!que.empty()) {
int u=que.front(); que.pop();
// 找到一條滿足X->Y的交替路徑
if(Y[u]){ f=u; break; }
if(I[u]) {
// u->v,交換之后滿足M1
rep(v,1,m) if(!P[v] && !I[v] && A[W[v]]+(W[u]==W[v])) P[v]=u,que.push(v);
} else {
// 滿足擬陣M2,即交換這條邊之后還是生成樹,可以從目前生成樹上的環上選取能夠替換的邊
int x=U[u],y=V[u];
if(Find(x)!=Find(y)) {
rep(v,1,m) if(!P[v] && I[v])
P[v]=u,que.push(v);
} else {
while(x!=y) {
if(dep[x]<dep[y]) swap(x,y);
int i=fe[x];
if(!P[i]) P[i]=u,que.push(i);
x=fa[x];
}
}
}
}
if(!f) break;
// 放置交替方案
while(~f) {
A[W[f]]+=I[f]?1:-1;
I[f]^=1,f=P[f];
}
int cnt=0;
rep(i,1,m) if(I[i]) cnt++;
// 重構並查集
rep(i,1,n) F[i]=i;
rep(i,1,m) if(I[i]) F[Find(U[i])]=Find(V[i]);
}
int cnt=0;
rep(i,1,m) if(!I[i]) cnt++;
printf("%d\n",cnt);
rep(i,1,m) if(!I[i]) printf("%d ",i);
}