2018年3月1日训练日记

今天看了 一般图匹配的带花树算法

看了几个大佬的博客,也是有点懵:

寻找增广路的做法是:从一个还没被匹配的点(exposed vertex)出发,中间形成交错路径,最后停止在一个没被匹配的点,这就是一条增广路。
定义:在路径上给这些点从1开始标号,奇数的点我们称为外点,偶数的点我们称为内点,可以发现他们恰好对应两个集合(X为外点的集合,Y为内点的集合)

直接寻找增广路做法不适用于一般图匹配的原因:寻找增广路时会形成环,导致有些点既是内点又是外点。找到增广路时都会把未匹配边变成匹配边,匹配边变成未匹配边,若是有些点既是外点又是内点,会匹配出错,即一个点存在于两个匹配中

注意:一般图中只会出现root连两条未匹配边,因为寻找增广路都是从未匹配点开始的。

带花树的做法是:
像匈牙利算法那样不断枚举点寻找增广路,
当找到环时,找到u和v的最近公共祖先,
分别从u和v跑到最近公共祖先的过程中,把环里面的边从有向变成无向的,并环中点的所在集合都设为root

2018年3月1日训练日记_第1张图片

(以上内容大部分为大佬的博客部分内容)

题目:

Ural-1099 Work scheduling 带花树模板题

ZJU 3316 Game 是否存在完美匹配,如果不是先手就可以走未匹配的点,必赢

HDU 3446 daizhenyang's chess  两次匹配,第一次不带king第二次带。若匹配数增加就说明king开始有一条增广路,必赢。

HDU 4687 Boke and Tsukkomi 先求出最大匹配sum,然后枚举每个匹配,删除这个匹配再求最大匹配,若为sum-1就说明这个匹配不是多余的,否则就是多余的

HDU 3551 hard problem 因为要删边使度数减少,所以先拆边,每条边拆成两个点,连边,然后我们把每个点拆成deg[i]-D[i]个点(deg[i]为原图度数,D[i]为子图度数),然后对所有该点和他的边(i,e)组成的点连一条边,表示这个点缺少的度数,可以由这儿得到。接着,求匹配,如果是完美匹配,即:每个度数都找到了可以使他减少的边。这个子图可以得到。

代码还是比较长的。。。

附个人带花树算法模板(Ural-1099 Work scheduling):

#pragma comment(linker,"/STACK:102400000,102400000")
#include
#include
#include
#include
#include
#include
#include
#define maxn 250
#define maxm maxn*maxn*2
#define INF 1e9
using namespace std;
inline int read()
{
    register int c=getchar(),fg=1,sum=0;
    while(c>'9'||c<'0') {if(c == '-')fg = -1;c=getchar();}
    while(c<='9'&&c>='0'){sum=sum*10+c-'0';c=getchar();}
    return fg*sum;
}
dequeQ;
bool lk[maxn][maxn];
int n,m,k,p,ans,sum,g[maxm],ql,qr,pre[maxn],base[maxn],tim;
bool inq[maxn],inb[maxn],inp[maxn];
struct node{
 int u,v;
}e[maxm];
int lca(int x,int y){
 memset(inp,0,sizeof(inp));
 while(1){
     x=base[x];
     inp[x]=1;
     if(g[x]==-1) break;
     x=pre[g[x]];
 }
 while(1){
     y=base[y];
     if(inp[y]) return y;
     y=pre[g[y]];
}
}
void sk(int x,int p){
    while(x!=p)
    {
        int y=g[x];
        inb[base[x]]=1;
        inb[base[y]]=1;
        y=pre[y];
        if(base[y]!=p) pre[y]=g[x];
        x=y;
    }
}
void ct(int x,int y,int n)
{
 int anc=lca(x,y);
 memset(inb,0,sizeof(inb));
 sk(x,anc);sk(y,anc);
 if(base[x]!=anc) pre[x]=y;
 if(base[y]!=anc) pre[y]=x;
 for(int i=1;i<=n;i++)
 {
     if(inb[base[i]]){
         base[i]=anc;
         if(!inq[i]){
             Q.push_back(i);
             inq[i]=1;
         }
     }
 }
}
bool dfs(int s,int n){
    for(int i=0;i<=n;i++) {pre[i]=-1;inq[i]=0;base[i]=i;}
    Q.clear();Q.push_back(s);inq[s]=1;
    while(!Q.empty()){
        int u=Q.front();Q.pop_front();
        for(int v=1;v<=n;v++)
        {
           if(lk[u][v]&&base[v]!=base[u]&&g[u]!=v)
           {
               if(v==s||(g[v]!=-1&&pre[g[v]]!=-1)) ct(u,v,n);
               else if(pre[v]==-1){
                pre[v]=u;
                if(g[v]!=-1){
                    Q.push_back(g[v]);
                    inq[g[v]]=1;
                    }
                else {
                    u=v;
                    while(u!=-1)
                    {
                        v=pre[u];
                        int w=g[v];
                        g[u]=v;
                        g[v]=u;
                        u=w;
                    }
                    return 1;
                }
               }
           }
        }
    }
    return 0;
}
int solve()
{
    int ans=0;
    memset(g,-1,sizeof(g));
    for(int i=1;i<=n;i++)
    if(g[i]==-1&&dfs(i,n)) ans++;
    return ans;
}
int main()
{
    int T,cas=1,x,y,c;
    scanf("%d",&n);
    {
     memset(lk,0,sizeof(lk));
     int u,v;
     while(scanf("%d%d",&u,&v)==2)
     {lk[u][v]=lk[v][u]=1;}
     int sum=solve();
     printf("%d\n",sum*2);
    for(int i=1;i<=n;++i){
    if(i

另外,贴上F题用到的二分图最大匹配高效率的HK算法个人模板(HDU 2389):复杂度O(sqrt(n)*E)

#pragma comment(linker,"/STACK:102400000,102400000")
#include
#include
#include
#include
#include
#include
#include
#define maxn 3010
#define INF 1e9
using namespace std;
inline int read()
{
    register int c=getchar(),fg=1,sum=0;
    while(c>'9'||c<'0') {if(c == '-')fg = -1;c=getchar();}
    while(c<='9'&&c>='0'){sum=sum*10+c-'0';c=getchar();}
    return fg*sum;
}
int n,m,k,p,ans,sum;
vectorG[maxn];
int mx[maxn],my[maxn];
int dx[maxn],dy[maxn];
int dis;
bool used[maxn];
bool findp()
{
    queue Q;
    dis=INF;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=0;idis) break;
        for(int i=0;i

你可能感兴趣的:(训练日记,图论,二分图)