1.二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。
2.二分图无奇数圈。
3.用DFS染色判断(代码如下):
int color[maxn];
bool bipartite(int u){
//判断结点u所在的连通分量是否为二分图
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(color[v]==color[u]) return false;
if(!color[v]){
color[v]=3-color[u];
if(!bipertite(v)) return false;
}
}
return true;
}
1.匹配是指两两没有公共点的边集。
2.最大匹配问题:给出一个二分图,找出一个边数最大的匹配。
3.完美匹配:图中所有点都是匹配点,|X|=|Y|。
4.完美匹配一定是最大匹配,而最大匹配不一定是完美匹配。
1.从一个匹配点出发,依次经过非匹配边,匹配边,非匹配边……形成的路径叫交替路。两个端点都是未盖点->增广路(Augmenting Path,简称AP)。
2.增广路上非匹配边比匹配边数量多一,如果将匹配边改为未匹配边,反之亦然,则匹配大小会增加一且依然是交错路。
3.当找不到增广路的时候,得到最大匹配。
1.由增广路的性质,增广路中的匹配边总是比未匹配边多一条,所以如果我们放弃一条增广路中的匹配边,选取未匹配边作为匹配边,则匹配的数量就会增加。匈牙利算法就是在不断寻找增广路,如果找不到增广路,就说明达到了最大匹配。
2.样例程序:
(1)输入格式:
第1行3个整数,V1,V2的节点数目 ,G的边数m.
第2到m+1行,每行两个整数x,y,代表 V1中编号为x的点和V2中编号为y的点之间有边相连.
(2)输出格式:
1个整数ans,代表最大匹配数
#include
#include
using namespace std;
int map[105][105];
int visit[105],flag[105];
int n,m;
bool dfs(int a){
for(int i=1; i<=n; i++){
if(map[a][i]&&!visit[i]){
visit[i]=1;
if(flag[i]==0||dfs(flag[i])){
flag[i]=a;
return true;
}
}
}
return false;
}
int main(){
int n1,n2;
while(cin>>n1 >>n2 >>m){
n=n1;
memset(map,0,sizeof(map));
for(int i=1; i<=m; i++){
int x,y;
cin>>x>>y;
map[x][y]=1;
}
memset(flag,0,sizeof(flag));
int result=0;
for(int i=1; i<=n1; i++){
memset(visit,0,sizeof(visit));
if(dfs(i)) result++;
}
cout<<result<<endl;
}
return 0;
}
3.因为增广路长度为奇数,路径起始点非左即右,所以我们先考虑从左边的未匹配点找增广路。注意到因为交错路的关系,增广路上的第奇数条边都是非匹配边,第偶数条边都是匹配边,于是左到右都是非匹配边,右到左都是匹配边。
4.那么只要从起始点开始 DFS 遍历直到找到某个未匹配点,O(m),因为要枚举n个点,总复杂度为O(nm)。
Uva 1663 Purifying Machine
(转自https://www.cnblogs.com/20143605–pcx/p/5140314.html)
题意:
每一个01串中最多含有一个星号,星号既可表示0也可表示1,给出一些等长的这样的01串,问最少能用多少个这样的串表示出这些串。如:000、010、0*1表示000、010、001、011,最少只需用“00星号”、“01星号”这两个即可表示出来。
分析:
如果有两个串只有一个位置上的数字不同,就可以用星号代替这个位置上的数,这样就能把两个串用一个串表示出来。因为要找最少的数目,当然星号用的越多越好,也就是说只需找出最多的对数,然后再加上不用星号表示的串的数目便是最小值。
两两判断,如果二者的二进制只有一位不同,在s1和s2之间连一条边,对应一个带星号的模板串,其中星号的位置就是s1和s2不同的那一位。
#include
#include
#include
#include
#include
using namespace std;
vector<int>b;
int vis[1005];
int n,m,a[1500][1500],s[1500],t[1005];
int A[11]={1,2,4,8,16,32,64,128,256,512,1024};
void f(string p)//二进制转换为十进制
{
int r=0;
for(int i=0;i<m;++i)
r=r*2+p[i]-'0';
b.push_back(r);
}
bool ok(int x,int y)
{
int z=x^y;
for(int i=0;i<11;++i)
if(z==A[i]) return true;
return false;
}
void setG()
{
memset(a,0,sizeof(a));
for(int i=0;i<n;++i)
for(int j=i+1;j<n;++j)
if(ok(b[i],b[j])) a[i][j]=a[j][i]=1;
}
bool match(int u)
{
for(int v=0;v<n;++v){
if(!a[u][v]) continue;
if(vis[v]) continue;
vis[v]=1;
if(t[v]==-1||match(t[v])){
t[v]=u;
s[u]=v;
return true;
}
}
return false;
}
void solve()
{
memset(s,-1,sizeof(s));
memset(t,-1,sizeof(t));
int ans=0;
for(int i=0;i<n;++i)
if(s[i]==-1){
memset(vis,0,sizeof(vis));
if(match(i)) ++ans;
}
ans/=2;
for(int i=0;i<n;++i)
if(t[i]==-1) ++ans;
printf("%d\n",ans);
}
int main()
{
string p;//n个长度为m的01串
while(scanf("%d%d",&m,&n)&&(n+m))
{
b.clear();
for(int i=0;i<n;++i){
cin>>p;
int j;
for(j=0;j<m;++j)
if(p[j]=='*') break;
if(j==m) f(p);
else{//若有* ,则转化为其代表的两个串,分别对应
p[j]='0';
f(p);
p[j]='1';
f(p);
}
}
sort(b.begin(),b.end());
n=unique(b.begin(),b.end())-b.begin();
setG();
solve();
}
return 0;
}