HIT暑期集训 二分图匹配

匈牙利求最大匹配数模板

int dfs(int x)
{
    for (int i=last[x];i;i=nxt[i])
    {
        if (!used[to[i]])
        {
            used[to[i]]=1;
            if (!match[to[i]] || dfs(match[to[i]]))
            {
                match[to[i]]=x;
                return 1;
             }
         }
    }
    return 0;
}
匈牙利(部分)

KM最大权匹配模板

int dfs(int x)
{
    int i,tmp;
    vis_g[x]=1;
    for (i=1;i<=n;i++)
    {
        if (vis_b[i]) continue;
        tmp=ex_g[x]+ex_b[i]-mp[x][i];
        if (tmp==0)
        {
            vis_b[i]=1;
            if (!match[i] || dfs(match[i]))
            {
                match[i]=x;
                return 1;
            }
        }
        else slack[i]=min(slack[i],tmp);
    }
    return 0;
}
int km()
{
    int i,j,re=0,tmp;    
    for (i=1;i<=n;i++)
    {
        ex_g[i]=0;ex_b[i]=0;
        match[i]=0;
        for (j=1;j<=n;j++) ex_g[i]=max(ex_g[i],mp[i][j]);    
    }
    for (i=1;i<=n;i++)
    {
        for (j=1;j<=n;j++) slack[j]=inf;
        while (1)
        {
            memset(vis_g,0,sizeof(vis_g));
            memset(vis_b,0,sizeof(vis_b));
            if (dfs(i)) break;
            tmp=inf;
            for (j=1;j<=n;j++)
                if (!vis_b[j]) tmp=min(tmp,slack[j]);
            for (j=1;j<=n;j++) 
            {
                if (vis_g[j]) ex_g[j]-=tmp;
                if (vis_b[j]) ex_b[j]+=tmp;
                else slack[j]-=tmp;
            }
        }
    }
    for (i=1;i<=n;i++)
        re+=mp[match[i]][i];
    return re;
}
km最大权匹配

C    HDU 1045

建图+最大匹配。

我们为每一行中被墙隔开的每一部分编上序号,为每一列中被墙隔开的每一部分也编上序号。对于每一个单位,当它被安上炮楼时意味着它对应的“行”和“列”(处理后的行和列)内不可放置炮楼,因此在每一个单位的“行”序号和“列”序号之间连边,求出的最大匹配就是能放置炮楼的最大数。(这题的代码几乎和E题一模一样)

不过因为数据很小(n<=4),好像也可以直接搜索。。

#include<cstdio>
#include<cstring>
#define maxn 20
#define maxm 20
using namespace std;
int num,last[maxn],to[maxm],nxt[maxm],used[maxn],match[maxn],cnt;
int a[maxn][maxn],b[maxn][maxn],cnta,cntb;
char mp[maxn][maxn];
int dfs(int x)
{
    for (int i=last[x];i;i=nxt[i])
    {
        if (!used[to[i]])
        {
            used[to[i]]=1;
            if (!match[to[i]] || dfs(match[to[i]]))
            {
                match[to[i]]=x;
                return 1;
             }
         }
    }
    return 0;
}
void add(int x,int y)
{
    to[++num]=y;nxt[num]=last[x];last[x]=num;
}
int main()
{
    int i,j,n,ans;
    while (scanf("%d",&n)!=EOF && n!=0)
    {
        for (i=1;i<=n;i++) scanf("%s",mp[i]+1);
        cnta=0;cntb=0;ans=0;num=0;
        memset(match,0,sizeof(match));
        memset(last,0,sizeof(last));
        for (i=1;i<=n;i++)
        for (j=1;j<=n;j++)//竖向 
        {
            if (mp[i][j]=='X') continue;
            if (i!=1 && mp[i-1][j]=='.') a[i][j]=a[i-1][j];
            else a[i][j]=++cnta;
        }
        for (i=1;i<=n;i++)
        for (j=1;j<=n;j++)//横向 
        {
            if (mp[i][j]=='X') continue;
            if (j!=1 && mp[i][j-1]=='.') b[i][j]=b[i][j-1];
            else b[i][j]=++cntb;
            add(a[i][j],b[i][j]);
        }
        for (i=1;i<=cnta;i++)
        {
            memset(used,0,sizeof(used));
            ans+=dfs(i);
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

D    HDU 2448

E    POJ 2226

建图+最小顶点覆盖。

对于每个一个单位的水洼,它既可以被横着的木板遮盖,也可以被竖着的木板遮盖。于是我们将每一条横着遮盖水洼的木板、每一条竖着遮盖水洼的木板分别编上号,对于每个一个单位的水洼在能够遮盖它的横木板和竖木板之间连边,可以发现这条边代表了这个水洼。由于对于每个水洼(边)要有一条木板(该边的两端点)遮盖,问题便转化为了求这个二分图的最小顶点覆盖。由于最小顶点覆盖等于最大匹配,于是这道题就可以当作二分图匹配做了。

#include<cstdio>
#include<cstring>
#define maxn 2505
#define maxm 2505
using namespace std;
int num,last[maxn],to[maxm],nxt[maxm],used[maxn],match[maxn],cnt;
int a[maxn][maxn],b[maxn][maxn],cnta,cntb;
char mp[maxn][maxn];
int dfs(int x)
{
    for (int i=last[x];i;i=nxt[i])
    {
        if (!used[to[i]])
        {
            used[to[i]]=1;
            if (!match[to[i]] || dfs(match[to[i]]))
            {
                match[to[i]]=x;
                return 1;
             }
         }
    }
    return 0;
}
void add(int x,int y)
{
    to[++num]=y;nxt[num]=last[x];last[x]=num;
}
int main()
{
    int i,j,n,m,ans;
    while (scanf("%d%d",&n,&m)!=EOF)
    {
        for (i=1;i<=n;i++) scanf("%s",mp[i]+1);
        cnta=0;cntb=0;ans=0;num=0;
        memset(match,0,sizeof(match));
        memset(last,0,sizeof(last));
        for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)//竖向 
        {
            if (mp[i][j]!='*') continue;
            if (i!=1 && mp[i-1][j]=='*') a[i][j]=a[i-1][j];
            else a[i][j]=++cnta;
        }
        for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)//横向 
        {
            if (mp[i][j]!='*') continue;
            if (j!=1 && mp[i][j-1]=='*') b[i][j]=b[i][j-1];
            else b[i][j]=++cntb;
            add(a[i][j],b[i][j]);
        }
        for (i=1;i<=cnta;i++)
        {
            memset(used,0,sizeof(used));
            ans+=dfs(i);
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

F    HDU 3718

H    POJ 2060

I    HDU 2819

J    HDU 2853

posted @ 2020-08-14 22:28  lsy_kk  阅读(155)  评论(0)    收藏  举报