kuangbin带你飞 专题十 匹配问题

匹配主要就是二分图匹配和一般图匹配,一般图的带花树算法我还不会

二分图匹配主要就是匈牙利算法,也可以用最大流解决

具体的看blog:http://www.renfei.org/blog/bipartite-matching.html 讲的比较清楚了

今天做了几个简单题

hdu 1045

题意:给你个棋盘,一部分地方不能放,然后问你最多放多少个,同一行同一列不能有多个,不过如果两个同行或者同列之间隔着一个墙,就能放

题解:这题就是建图难,一开始想不到吧,可以把没有分开的一段行和列缩成一个点,然后如果这段行和某点列有相交部分,就是这两个点之间有边,然后最多放多少个棋子,就是最多取多少条边,就是二分图的最大匹配问题了

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX       1005
#define   MAXN      2000005
#define   lson      l,m,rt<<1
#define   rson      m+1,r,rt<<1|1
#define   lrt       rt<<1
#define   rrt       rt<<1|1
#define   mid       int m=(r+l)>>1
#define   LL        long long
#define   ull       unsigned long long
#define   mem0(x)   memset(x,0,sizeof(x))
#define   mem1(x)   memset(x,-1,sizeof(x))
#define   meminf(x) memset(x,INF,sizeof(x))
#define   lowbit(x) (x&-x)

const LL     mod   = 1000000;
const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;

int edge[20][20];
int head[MAX];
int row[5][5];
int col[5][5];
int match[MAX];
int used[MAX];
int tot;
int cnt;
int n;

void init(){
    mem0(edge);
    mem1(match);
    mem0(col);
    mem0(row);
    tot=0;
    cnt=0;
}

bool dfs(int u){
    used[u]=1;
    for(int i=1;i<=cnt;i++){
        if(!edge[u][i]) continue;
        int w=match[i];
        if(w<0||(!used[w]&&dfs(w))){
            match[i]=u;
            match[u]=i;
            return true;
        }
    }
    return false;
}

int main(){
    string s[5];
    while(scanf("%d",&n)&&n){
        init();
        for(int i=0;i<n;i++) cin>>s[i];
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if(s[i][j]=='.'&&j!=0&&s[i][j-1]=='.') row[i][j]=cnt;
                else if(s[i][j]=='.') row[i][j]=++cnt;
            }
        }
        for(int j=0;j<n;j++){
            for(int i=0;i<n;i++){
                if(s[i][j]=='.'&&i!=0&&s[i-1][j]=='.') col[i][j]=cnt;
                else if(s[i][j]=='.') col[i][j]=++cnt;
            }
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if(s[i][j]=='.'){
                    edge[row[i][j]][col[i][j]]=edge[col[i][j]][row[i][j]]=1;
                }
            }
        }
        int ans=0;
        for(int i=1;i<=cnt;i++){
            if(match[i]<0){
                mem0(used);
                if(dfs(i)) ans++;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}



hdu 2444

题意:给你一些朋友关系,然后问把他们分成两组,每组中都不是朋友,第一组中每个人在第二组中都有一个朋友,明显就是二分图的最大匹配,不过就是可能给你的图有环,问你能不能构成二分图

题解:就是首先判断是否是二分图,这个用染色算法,就是dfs,如果搜到一条边上两端颜色相同就显然不是二分图。

判断完是二分图之后直接就匈牙利上了

数组貌似比较恶心,要开的大一点

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX       1005
#define   MAXN      2000005
#define   lson      l,m,rt<<1
#define   rson      m+1,r,rt<<1|1
#define   lrt       rt<<1
#define   rrt       rt<<1|1
#define   mid       int m=(r+l)>>1
#define   LL        long long
#define   ull       unsigned long long
#define   mem0(x)   memset(x,0,sizeof(x))
#define   mem1(x)   memset(x,-1,sizeof(x))
#define   meminf(x) memset(x,INF,sizeof(x))
#define   lowbit(x) (x&-x)

const LL     mod   = 1000000;
const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;

struct Edge{
    int v,next;
}edge[1000005];
int head[MAX];
int col[MAX];
int match[MAX];
int used[MAX];
int tot;

void add_edge(int a,int b){
    edge[tot]=(Edge){b,head[a]};
    head[a]=tot++;
}

void init(){
    mem1(head);
    mem1(match);
    mem0(col);
    tot=0;
}

bool colour(int u){
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(!col[v]){
            col[v]=-col[u];
            if(!colour(v)) return false;
        }
        else if(col[v]==col[u]) return false;
    }
    return true;
}

bool dfs(int u){
    used[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        int w=match[v];
        if(w<0||(!used[w]&&dfs(w))){
            match[v]=u;
            match[u]=v;
            return true;
        }
    }
    return false;
}

int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        init();
        for(int i=0;i<m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            add_edge(a,b);
            add_edge(b,a);
        }
        int flag=0;
        for(int i=1;i<=n;i++){
            if(!col[i]){
                col[i]=1;
                if(!colour(i)){
                    printf("No\n");
                    flag=1;
                    break;
                }
            }
        }
        if(!flag){
            int ans=0;
            for(int i=1;i<=n;i++){
                if(match[i]<0){
                    mem0(used);
                    if(dfs(i)) ans++;
                }
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}


hdu 1083

全裸的匈牙利一套带走

不说了


hdu 1281

题意:就是每行每列只能有一个车,然后有些点可以放车,不能放的点不影响(和第一题棋盘不同,我开头看错了)

重要点就是如果这个地方不放,那么就会影响最大数量

题解:有了第一题的经验,这题很明显就是行列缩成一个点,车所在的点就是行列相交的地方,就是行(点)和列(点)之间连接的边,然后墙壁不影响,更容易了,直接给你的坐标建图,然后先求出最大匹配,再枚举删除每条边,如果得到的匹配数不等于最大匹配,那么这个点就是重要点。

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX       205
#define   MAXN      2000005
#define   lson      l,m,rt<<1
#define   rson      m+1,r,rt<<1|1
#define   lrt       rt<<1
#define   rrt       rt<<1|1
#define   mid       int m=(r+l)>>1
#define   LL        long long
#define   ull       unsigned long long
#define   mem0(x)   memset(x,0,sizeof(x))
#define   mem1(x)   memset(x,-1,sizeof(x))
#define   meminf(x) memset(x,INF,sizeof(x))
#define   lowbit(x) (x&-x)

const LL     mod   = 1000000;
const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;

int edge[MAX][MAX];
int match[MAX];
int used[MAX];
int n,m,k;

void init(){
    mem0(edge);
    mem1(match);
}

bool dfs(int u){
    used[u]=1;
    for(int i=1;i<=n+m;i++){
        if(!edge[u][i]) continue;
        int w=match[i];
        if(w<0||(!used[w]&&dfs(w))){
            match[i]=u;
            match[u]=i;
            return true;
        }
    }
    return false;
}

int main(){
    int kase=0;
    while(~scanf("%d%d%d",&n,&m,&k)){
        init();
        kase++;
        for(int i=0;i<k;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            edge[a][b+n]=edge[b+n][a]=1;
        }
        int ans=0;
        for(int i=1;i<=n+m;i++){
            if(match[i]<0){
                mem0(used);
                if(dfs(i)) ans++;
            }
        }
        int ret=0;
        for(int i=1;i<=n+m;i++){
            for(int j=i+1;j<=n+m;j++){
                if(edge[i][j]){
                    edge[i][j]=edge[j][i]=0;
                    mem1(match);
                    int tmp=0;
                    for(int k=1;k<=n+m;k++){
                        if(match[k]<0){
                            mem0(used);
                            if(dfs(k)) tmp++;
                        }
                    }
                    if(tmp!=ans) ret++;
                    edge[i][j]=edge[j][i]=1;
                }
            }
        }
        printf("Board %d have %d important blanks for %d chessmen.\n",kase,ret,ans);
    }
    return 0;
}

hdu 2819

 题意:给你一个01矩阵,问你能不能通过行列交换,使对角线都是1

题解:首先要知道,如果i行j列是1,那么就是点i->j+n有边,所以对角线都为1,就是说最多可以连n条边,就是最大匹配是n

把行和列缩点,求出最大匹配,如果是n,然后就是看交换多少次,要知道变成对角线为1,只用行变换或者只用列变换都可以做到,所以枚举每行,看对应的行match的是不是这一列,如果不是就找下面满足的行和它换(其实挺水的,一开始没认真做,题目都没怎么搞懂)


#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX       205
#define   MAXN      2000005
#define   lson      l,m,rt<<1
#define   rson      m+1,r,rt<<1|1
#define   lrt       rt<<1
#define   rrt       rt<<1|1
#define   mid       int m=(r+l)>>1
#define   LL        long long
#define   ull       unsigned long long
#define   mem0(x)   memset(x,0,sizeof(x))
#define   mem1(x)   memset(x,-1,sizeof(x))
#define   meminf(x) memset(x,INF,sizeof(x))
#define   lowbit(x) (x&-x)

const LL     mod   = 1000000;
const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;

struct Edge{
    int v,next;
}edge[20005];
int head[MAX];
int row[MAX];
int match[MAX];
int used[MAX];
int tot;

void add_edge(int a,int b){
    edge[tot]=(Edge){b,head[a]};
    head[a]=tot++;
}

void init(){
    mem1(head);
    mem1(match);
    mem0(row);
    tot=0;
}

bool dfs(int u){
    used[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        int w=match[v];
        if(w<0||(!used[w]&&dfs(w))){
            match[v]=u;
            match[u]=v;
            return true;
        }
    }
    return false;
}

int main(){
    int n;
    while(~scanf("%d",&n)){
        init();
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                int a;
                scanf("%d",&a);
                if(a){
                    add_edge(i,j+n);
                    add_edge(j+n,i);
                }
            }
        }
        int ans=0;
        for(int i=1;i<=2*n;i++){
            if(match[i]<0){
                mem0(used);
                if(dfs(i)) ans++;
            }
        }
        if(ans!=n) printf("-1\n");
        else{
            int ret=0;
            for(int i=1;i<=n;i++){
                int k=match[i]-n;
                if(k==i) continue;
                for(int j=i+1;j<=n;j++){
                    int t=match[j]-n;
                    if(t==i){
                        ret++;
                        row[i]=j;
                        swap(match[i],match[j]);
                    }
                }
            }
            printf("%d\n",ret);
            for(int i=1;i<=n;i++){
                if(row[i]) printf("R %d %d\n",i,row[i]);
            }
        }
    }
    return 0;
}


hdu 2389

题意:就是给你6000个点,分成两堆,给你坐标,然后给你人的速度,如果在时间内这个人能跑到伞所在的坐标,那么这两个点之间就有边,这样的话最多有3000×3000的边

匈牙利就会TLE,用网络流的ISAP要建反向边1800W伤不起,所以就去学了HK算法,详见http://www.cnblogs.com/penseur/archive/2013/06/16/3138981.html

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX       6005
#define   MAXN      2000005
#define   lson      l,m,rt<<1
#define   rson      m+1,r,rt<<1|1
#define   lrt       rt<<1
#define   rrt       rt<<1|1
#define   mid       int m=(r+l)>>1
#define   LL        long long
#define   ull       unsigned long long
#define   mem0(x)   memset(x,0,sizeof(x))
#define   mem1(x)   memset(x,-1,sizeof(x))
#define   meminf(x) memset(x,INF,sizeof(x))
#define   lowbit(x) (x&-x)

const LL     mod   = 1000000;
const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;

/**************读入外挂**********************/
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}
/*******************************************/

struct Edge{
    int v,next;
}edge[10000005];

struct Node{
    int x,y,z;
}node[MAX];
int head[MAX];
int match[MAX];
int used[MAX];
int dis[MAX];
int tot;
int T,t,n,m,diss;

void add_edge(int a,int b){
    edge[tot]=(Edge){b,head[a]};
    head[a]=tot++;
}

void init(){
    mem1(head);
    mem1(match);
    tot=0;
}

bool bfs(){
    diss=INF;
    mem1(dis);
    queue<int> q;
    for(int i=1;i<=n;i++){
        if(match[i]==-1){
            dis[i]=0;
            q.push(i);
        }
    }
    while(!q.empty()){
        int u=q.front();
        q.pop();
        if(dis[u]>diss) break;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(dis[v]==-1){
                dis[v]=dis[u]+1;
                if(match[v]==-1) diss=dis[v];
                else {
                    dis[match[v]]=dis[v]+1;
                    q.push(match[v]);
                }
            }
        }
    }
    return diss!=INF;
}

bool dfs(int u){
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(!used[v]&&dis[v]==dis[u]+1){
            used[v]=1;
            if(match[v]!=-1&&diss==dis[v]) continue;
            if(match[v]==-1||dfs(match[v])){
                match[u]=v;
                match[v]=u;
                return true;
            }
        }
    }
    return false;
}

int MaxMatch(){
    int ans=0;
    while(bfs()){
        mem0(used);
        for(int i=1;i<=n;i++){
            if(match[i]==-1&&dfs(i)) ans++;
        }
    }
    return ans;
}

int main(){
    scanf("%d",&T);
    for(int kase=1;kase<=T;kase++){
        scanf("%d%d",&t,&n);
        init();
        for(int i=1;i<=n;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            node[i]=(Node){a,b,c};
        }
        scanf("%d",&m);
        for(int i=1;i<=m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            for(int j=1;j<=n;j++){
                int aa=node[j].x-a;
                int bb=node[j].y-b;
                int cc=t*node[j].z;
                if(cc*cc>=aa*aa+bb*bb){
                    add_edge(j,i+n);
                }
            }
        }
        printf("Scenario #%d:\n%d\n\n",kase,MaxMatch());
    }
    return 0;
}

题解:套一发模板就好了


你可能感兴趣的:(算法,ACM)