グラフ理論 - カバーとDAGの独立したセット

[概要]

カバーされた問題の別個のセットと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;
}

[例]

リリース1871元の記事 ウォンの賞賛702 ビュー194万+

おすすめ

転載: blog.csdn.net/u011815404/article/details/102649987