記事のディレクトリ
1. 2部グラフとは何ですか?
グラフの頂点を2つのセットXとYに分割でき、グラフのすべてのエッジで、一方の頂点がセットXに属し、もう一方の頂点がセットYに属している必要がある場合、そのグラフは「二分割グラフ」と呼ばれます。 "または"二者間グラフ "
2部グラフのアプリケーションでは、最も一般的なものは最大マッチングを求めることです。他の多くの問題は、解決するためにマッチング問題に変換できます。
2部グラフの最大一致を見つける方法は?----ここではハンガリーのアルゴリズムが使用されています
簡単な例を見てみましょう。
たとえば、男性と女性のコロケーションの問題、上の頂点は男の子、下の頂点は女の子、接続のある2つのポイントをペアにすることができ、ペアの最大数が必要になります。
- 最初に男の子を左側に置き、最初の男の子から一致する女の子を探し始めます。これは男性1->女性1で、次に2番目の男の子を見つけ、2番目の男の子も最初の女の子と一致することを確認します。最初の女の子が最初の男の子と一致しました。現時点では、ハンガリーのアルゴリズムの鍵は次のとおりです。男性2と一致する女の子が一致した場合、その女の子(男性1)に対応する男性を見つけます。男の子は他の女の子と対戦できます。はいの場合、対戦数は1増加し、そうでない場合はそれ以上増加しません。
- アルゴリズムの観点から、各男の子に対してDFS検索が実行され、一致する可能性があるが一致しない女の子が右側にあるかどうかが確認されます。女の子がすでに一致している場合は、対応する男の子(一致する)を見つけて確認します。男の子が一致しない女の子を見つけることができる場合など、一致する女の子が見つかるまで、アルゴリズムはn DFSを実行します(nは男の子の数です)
次に、2部グラフの最小頂点被覆
2部グラフで最小点を見つけて、各エッジが少なくとも1つの点に関連付けられるようにします。これは、2部グラフの最小頂点被覆です(最小固定小数点カバレッジは、最小点のすべてのエッジをカバーすることです)。
この例も見てください。
男性と女性の生徒が恋に落ちるのを防ぐために、学校は生徒を追放する必要があります。今必要なのは、この現象を回避するために何人の学生を追放できるかということです。答えは、これらの3つのポイントがすべての側面をカバーしているため、女性1、女性2、男性4を追放することです。
では、最小頂点被覆を見つける方法は?最初に見てみましょう、最大でいくつのペアが一致しますか?3つのペアもあります。たとえば、男性の4人が一致するなど、ペアが一致すると、彼と一致する可能性のある他の女の子は一致しなくなる、つまり削除されることがわかります。これらの3つのポイントが一致すると、他のすべてのポイントが一致します。ポイントも削除されます。
結論:2部グラフの固定小数点カバレッジの最小数= 2部グラフの一致の最大数
第三に、DAGグラフの最小パスカバレッジ
(有向非巡回グラフ)有向非巡回グラフのすべての頂点を、互いに素な単純なパスをできるだけ少なくしてカバーします。これは、DAGグラフの最小パスカバレッジの問題です(最小パスカバレッジは、パスが最も少ないすべてのポイントをカバーすることです)。
n点があり、一致する数が0の場合、n個のエッジが必要です。ペアが一致しない場合、1つのエッジが縮小されます。
DAGグラフの最小カバレッジ数=ノード数-最大一致数
第四に、二部グラフの最大の独立したセット
できるだけ多くの生徒がお互いに一致することなく活動に参加できるようにするために、選択された最大数の生徒(追放された生徒と同じ)を10人で連れて行くことができます(追放された3人を除く)
2部グラフの独立集合の最大数=ノードの数-一致の最大数
五、POJ—1325-マシンスケジュール
アイデア:この
質問は、実際には2部グラフの最小頂点カバレッジ問題です。マシンの再起動の最小数、つまり、すべてのエッジを最小のポイントでカバーするように求めます。以下のコードには詳細なコメントがあります
コード:
#include <iostream>
#include <cstring>
using namespace std;
const int MAXN=510;
int Left,Right;//left是匹配左边的顶点数,right是匹配右边的顶点数
int g[MAXN][MAXN];//邻接矩阵
int linker[MAXN];//存右点对象(linker[i]表示和女生i匹配的男生的编号)
int vis[MAXN];//右点是否访问过
int dfs(int l)
{
for(int r=0;r<Right;r++)
{
if(g[l][r]&&!vis[r])//左边到右边有边,并且右边该点未访问
{
vis[r]=1;
if(linker[r]==-1||dfs(linker[r]))
//如果右边的点还未匹配 或者 与该点匹配的左边的点可以找到另一个右点与其匹配,那么返回1
//该女生没有对象 或者 该女生的男朋友可以找到另一个对象 该女生就和该男生匹配,匹配数加一
{
linker[r]=l;
return 1;
}
}
}
return 0;
}
//匈牙利算法
int hungary()
{
int res=0;
memset(linker,-1,sizeof(linker));
for(int l=0;l<Left;l++)//n趟dfs
{
memset(vis,0,sizeof(vis));//防止单次dfs重复搜
if(dfs(l)) res++;
}
return res;
}
int main()
{
int k;
while(cin>>Left&&Left)
{
cin>>Right>>k;
memset(g,0,sizeof(g));
int id,lnum,rnum;
while(k--)
{
cin>>id>>lnum>>rnum;
if(lnum!=0&&rnum!=0)//等于0,不需要切换,可以直接完成
{
g[lnum][rnum]=1;
}
}
cout<<hungary()<<endl;
}
return 0;
}