[概要]
カバーされた問題の別個のセットとDAGでは、一般的な問題は、次の3つのカテゴリに分類されます。
- 最小頂点被覆パス
- 最小点が経路をカバーするように繰り返されてもよいです
- 独立したセットの最大数
問題のこれらの3つのタイプが解決するためにハンガリー二部グラフアルゴリズムを利用することができます。
[]最小のパスをカバー
最小パスが覆わ:有向非巡回グラフが与えられると、数として必要と互いに素のすべての頂点をカバーする、単純な経路は、非環式有向グラフ(各頂点が正確に一度だけ覆われています)
プロモーションKONING定理:DAG = DAGトップカバー最小パス・カウント - マッチした新しい二部グラフの最大数
新たな二部グラフのためのDAG図オリジナルG =(V、E)は、N = | V |スプリットポイント来ます。
- G点の数xが二点に分割され、それぞれは、X及びX + Nであります
- 1〜Nの左部分二部グラフの点は、図の二部の右側部分であるn + 1〜2N点
- 元の各有向辺の(x、y)は、左部分と接続された図の点y + N二部側の右側部との間の点x
- 得られた二部グラフGは、と呼ばれる分割点部グラフ Gzを
簡単に言えば、n個のDAGおよびmは、二部グラフは、新しく追加され、n個の点隣接行列、隣接行列の側面を構築するためにポイントが与えられます
int n,m;
bool vis[N];
int link[N];
bool G[N][N];
bool dfs(int x){
for(int y=1;y<=m;y++){
if(G[x][y]&&!vis[y]){
vis[y]=true;
if(link[y]==-1 || dfs(link[y])){
link[y]=x;
return true;
}
}
}
return false;
}
int hungarian(){
int ans=0;
for(int i=1;i<=n;i++){
memset(vis,false,sizeof(vis));
if(dfs(i))
ans++;
}
return ans;
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF&&(n+m)){
memset(link,-1,sizeof(link));
memset(G,true,sizeof(G));
while(m--){
int x,y;
scanf("%d%d",&x,&y);
G[x][y]=false;
}
int mate=hungarian();//最大匹配数
int res=n-mate;//最小路径点覆盖
printf("%d\n",res);
}
return 0;
}
【繰り返し最小パスポイントのカバレッジ/最大独立集合]
最大独立集合の任意の2点への接続は、濃縮されないように設定ポイントを選択します:
最小点は、経路を覆うように繰り返されてもよい。有向非巡回グラフが与えられると、数として必要と交差缶非環式グラフ(頂点が複数回覆われていてもよい)向けすべての頂点をカバーする、単純パス
最小繰り返し頂点表紙パスよく、もし二つの経路:... - > U - > P - > V - > ...と... - > X - > P - > Y - > ...点Pで交差するが、第2の経路に直接ダウンX-> yは、点pは、重複を避けることができるように、原画像のエッジ(x、y)を加えます。
間接通信Xのすべての元の点は、Yは直接上側(x、y)に接続されている場合、さらに、その後、被覆経路の最小点は繰り返すことができ、特定のに変換することができる最小のパスカバー
非循環有向グラフG最小経路が重なる点をカバーすることができるためにこのように、DAGに相当推移閉包シーク得られたGの新しいビューを「その後G」パスカバレッジポイントの最小要件
DAG最大独立集合を求め、そして、同じ考えポイントを繰り返すことができる最小のパスカバーを見つけます。
int n,m;
bool vis[N];
int link[N];
bool G[N][N];
bool dfs(int x){
for(int y=1;y<=n;y++){
if(G[x][y]&&!vis[y]){
vis[y]=true;
if(link[y]==-1 || dfs(link[y])){
link[y]=x;
return true;
}
}
}
return false;
}
int hungarian(){
int ans=0;
for(int i=1;i<=n;i++){
memset(vis,false,sizeof(vis));
if(dfs(i))
ans++;
}
return ans;
}
void floyd(){//传递闭包
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
G[i][j]|=G[i][k]&G[k][j];
}
int main(){
int t;
scanf("%d",&t);
while(t--){
memset(link,-1,sizeof(link));
memset(G,false,sizeof(G));
scanf("%d%d",&n,&m);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
G[x][y]=true;
}
floyd();//计算传递闭包
int mate=hungarian();//最大匹配数
int res=n-mate;//最大独立集、最小路径可重复点覆盖
printf("%d\n",res);
}
return 0;
}
[例]
- ドールズ(HDU-4160) (最小パスカバレッジ):こちらをクリック
- ロボット(POJ-1548) (最小パスカバレッジ):こちらをクリック
- AIR(POJ-1422)襲撃(最小パスカバレッジを):こちらをクリック
- スキームキャブタクシー(POJ-2060である)(最小マンハッタン距離のパスカバレッジ+) :こちらをクリック
- 会社の修復(POJ-3216) (ほとんどの短絡用+フロイド最小パスカバレッジ):こちらをクリック
- 現状IIおよびポッターハリー(HDU-3991) (ほとんどの短絡用+フロイド最小のパスカバレッジ):こちらをクリック
- 設定された最大到達不能(UVALive-8456)のノード(最大の独立したセット):こちらをクリック