2018年3月1日训练日记

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

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

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

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

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

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


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

题目:

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<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<vector>
#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;
}
deque<int>Q;
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<g[i])printf("%d %d\n",i,g[i]);
    }
    }
    return 0;
}

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

#pragma comment(linker,"/STACK:102400000,102400000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<vector>
#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;
vector<int>G[maxn];
int mx[maxn],my[maxn];
int dx[maxn],dy[maxn];
int dis;
bool used[maxn];
bool findp()
{
    queue<int> Q;
    dis=INF;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=0;i<n;i++)
    if(mx[i]==-1){
     Q.push(i);
     dx[i]=0;
    }
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop();
        if(dx[u]>dis) break;
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            if(dy[v]==-1)
            {
                dy[v]=dx[u]+1;
                if(my[v]==-1) dis=dy[v];
                else
                {
                 dx[my[v]]=dy[v]+1;
                 Q.push(my[v]);
                }
            }
        }
    }
    return dis!=INF;
}
bool dfs(int u)
{
    for(int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(!used[v]&&dy[v]==dx[u]+1)
        {
        used[v]=1;
        if(my[v]!=-1&&dy[v]==dis) continue;
        if(my[v]==-1||dfs(my[v]))
        {
         my[v]=u;
         mx[u]=v;
         return 1;
        }
        }
    }
    return 0;
}
int solve()
{
 int ans=0;
 memset(mx,-1,sizeof(mx));
 memset(my,-1,sizeof(my));
 while(findp())
 {
  memset(used,0,sizeof(used));
  for(int i=0;i<n;i++)
  if(mx[i]==-1&&dfs(i)) ans++;
 }
 return ans;
}
struct node{
    int x,y,s;
}n1[maxn],n2[maxn];
int col(node a,node b)
{
 return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
int main()
{
    int T,t,cas=0;
    T=read();
    while(T--)
    {
     ++cas;
     t=read();
     n=read();
     for(int i=0;i<=n;i++) G[i].clear();
     for(int i=0;i<n;i++)
     scanf("%d%d%d",&n1[i].x,&n1[i].y,&n1[i].s);
     m=read();
     for(int i=0;i<m;i++)
     scanf("%d%d",&n2[i].x,&n2[i].y);
     for(int i=0;i<n;i++)
     for(int j=0;j<m;j++)
     if(col(n1[i],n2[j])<=n1[i].s*n1[i].s*t*t)
     G[i].push_back(j);
     printf("Scenario #%d:\n",cas);
     printf("%d\n\n",solve());
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值