[kuangbin带你飞]专题十 匹配问题 二分匹配部分

刚回到家 开了二分匹配专题 手握xyl模板 奋力写写写 终于写完了一群模板题

A hdu1045

对这个图进行 行列的重写 给每个位置赋予新的行列 使不能相互打到的位置 拥有不同的行与列

然后左行右列 边是新的坐标 求最大匹配

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define L long long
char s[50][50];
int n ;
vector<int >q[50];
int a[50][50];
int b[50][50];
int m ;
bool vis[50];
int f[50];
bool fin(int u){
    for(int i=0;i<q[u].size();i++){
        int v =q[u][i];
        if(vis[v]){
            vis[v] = false;
            if(f[v] == -1 || fin(f[v])){
                f[v] = u ;
                return true;
            }
        }
    }
    return false;
}
int xyl(){
    int res = 0;
    memset(f,-1,sizeof(f));
    for(int i=1;i<=m;i++){
        memset(vis,true,sizeof(vis));
        if(fin(i)){
            res ++ ;
        }
    }
    return res  ;
}
int main(){
    while(scanf("%d",&n)!=EOF){
        if(n == 0)break;
        for(int i=1;i<=n;i++){
            scanf("%s",s[i] + 1);
        }
        for(int i = 0; i<= 40; i ++)q[i].clear();
        int cntx = 0;
        int cnt1 = 0;
        bool fl = false;
        for(int i=1;i<=n;i++){
            fl = false;
            for(int j=1;j<=n;j++){
                if(s[i][j] == 'X'){
                    fl = true;
                }
                else {
                    if(fl == true){
                        cntx ++ ;
                        fl = false;
                    }
                    a[i][j] = cntx + i;
                    cnt1 =a[i][j] ;
                }
            }
        }
        int cnty = 0;
        int cnt2 = 0;
        fl = false;
        for(int j=1;j<=n;j++){
            fl = false;
            for(int i=1;i<=n;i++){
                if(s[i][j] == 'X'){
                    fl = true;
                }
                else {
                    if(fl == true){
                        cnty ++ ;
                        fl = false;
                    }
                    b[i][j] = cnty + j ;
                    cnt2 = b[i][j] ;
                }
            }
        }
        for(int i = 1; i<=n ;i ++){
            for(int j = 1; j<=n; j ++) {
                if(s[i][j] != 'X'){
                    q[a[i][j]].push_back(b[i][j]);
                }
            }
        }
        m = max(cnt1,cnt2) ;
        int ans = xyl();
        printf("%d\n",ans);
    }
}

B hdu2444

double room的意思 不是两个房间 是双人间

所以题意就是判断二分图 顺便求最大匹配 

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define L long long
vector<int >q[205];
int n , m ;
int co[205];
bool vis[205];
bool bfs(int u){
    queue<int>que;
    que.push(u);
    co[u] = 0;
    while(!que.empty()){
        int f = que.front();que.pop();
        for(int i = 0; i< q[f].size(); i ++){
            int v = q[f][i];
            if(co[v] == -1){
                co[v] = co[f] ^ 1 ;
                que.push(v);
            }
            else {
                if(co[v] == co[f]){
                    return false;
                }
            }
        }
    }
    return true;
}
int linker[205];
bool fin(int u){
    for(int i=0;i<q[u].size();i++){
        int v = q[u][i];
        if(vis[v]){
            vis[v] = false ;
            if(linker[v] == -1 || fin(linker[v])){
                linker[v] = u ;
                return true;
            }
        }
    }
    return false;
}
int xyl(){
    int res = 0;
    memset(linker , -1 , sizeof(linker));
    for(int i = 1; i<= n ; i ++ ){
        memset(vis, true , sizeof(vis));
        if(fin(i)){
            res ++ ;
        }
    }
    return res ;
}
int main(){
    // freopen("in.cpp" , "r" , stdin);
    while(scanf("%d%d",&n,&m)!=EOF){
        for(int i = 1; i<=n ;i ++)q[i].clear();
        for(int i = 1; i<=m ;i ++){
            int a,b ;
            scanf("%d%d",&a, &b);
            q[a].push_back(b);
            q[b].push_back(a);
        }
        memset(co, -1 , sizeof(co));
        bool ok = true;
        for(int i = 1; i<=n ;i ++){
            if(co[i] == -1){
                if(!bfs(i)){
                    ok = false;
                }
            }
        }
        if(ok == false){
            printf("No\n");
            continue ;
        }
        int ans = xyl() ;
        printf("%d\n",ans/2);
    }
}

C hdu1083

一边课程 一边学生 最大匹配是否等于课程

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define L long long
int n , p ;
bool vis[305];
vector <int >q[305];
int linker[305];
bool fin(int u){
    for(int i = 0; i< q[u].size() ;i ++ ){
        int v = q[u][i];
        if(vis[v]){
            vis[v] = false;
            if(linker[v] == -1 || fin(linker[v])){
                linker[v] = u ;
                return true;
            }
        }
    }
    return false;
}
int xyl(){
    int res = 0 ;
    memset(linker , -1 ,sizeof(linker));
    for(int i = 1; i<= p;i ++) {
        memset(vis,true,sizeof(vis));
        if(fin(i)){
            res ++ ;
        }
    }
    return res ;
}
int main(){
    int t;
    scanf("%d",&t);
    while(t -- ){
        scanf("%d%d",&p,&n);
        for(int i = 1; i<= p; i ++){
            q[i].clear() ;
        }
        for(int i = 1; i<= p; i ++) {
            int x ;
            scanf("%d" , &x) ;
            for(int j = 1; j<= x; j ++ ){
                int z ;
                scanf("%d", &z );
                q[i].push_back(z);
            }
        }
        int ans = xyl () ;
        if(ans == p){
            printf("YES\n");
        }
        else {
            printf("NO\n");
        }
    }
}

D hdu1281

不可以放车的地方不影响攻击 那就不用重新写行列了

每个坐标都是一条边 求出最大匹配之后枚举每个坐标不可用 是不是重要点

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define L long long
int n , m ;
int x ;
vector<int >q[105];
int banx,bany;
int inx[10500];
int iny[10500];
int linker[105];
bool vis[105];
bool fin(int u){
    for(int i = 0; i< q[u].size() ;i ++ ) {
        int v = q[u][i];
        if(u == banx && v == bany){
            continue;
        }
        if(vis[v]){
            vis[v] = false;
            if(linker[v] == -1 || fin(linker[v])){
                linker[v] = u ;
                return true;
            }
        }
    }
    return false;
}
int xyl(){
    int res = 0 ;
    memset(linker , -1 ,sizeof(linker)) ;
    for(int i = 1; i<= n ;i ++){
        memset(vis,true,sizeof(vis));
        if(fin(i)){
            res ++ ;
        }
    }
    return res ;
}
int main(){
    //freopen("in.cpp" , "r" , stdin);
    int cas = 1;
    while(scanf("%d%d%d",&n,&m,&x)!=EOF){
        for(int i = 1; i<= 100; i ++)
            q[i].clear() ;
        for(int k = 1; k <= x ;k ++) {
            int i , j ;
            scanf("%d%d",&i,&j);
            q[i].push_back(j);
            inx[k] = i ;
            iny[k] = j ;
        }
        banx = 0;
        bany = 0;
        int maxx = xyl();
        int ans = 0;
        for(int i = 1; i<= x ; i ++ ){
            banx = inx[i];
            bany = iny[i];
            int res = xyl() ;
            if(res != maxx){
                ans ++ ;
            }
        }
        printf("Board %d have %d important blanks for %d chessmen.\n",cas ++ ,ans , maxx);
    }
}

E hdu2819 

这个用到了线性代数的知识

初等变换有三种 1 交换两行 2 一行*=k 3 一行 += 另一行*k

用其中的任意一种 都可以达到简化阶梯式(对角线上都是1)

并且 如果行变换可以 那么只用列变换也可以 

那么 建图的方法 左边是行 右边是列 边代表左边的行 可以提供右边的列数 即有 做列数的潜力(左边的这一行经过移动 在对角线上 交点是右边的那一列)

最后linker记录的就是 经过改变后 第i行 是原来的linker[i]行

处理后输出行的变换就可以了 并且有最大匹配 = n

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define L long long
int n ;
vector<int >q[105];
bool vis[105];
int linker[105];
bool fin(int u ){
    for(int i = 0; i< q[u].size(); i ++){
        int v = q[u][i];
        if(vis[v]){
            vis[v] = false;
            if(linker[v] == -1 || fin(linker[v])){
                linker[v] = u;
                return true;
            }
        }
    }
    return false;
}
int xyl(){
    int res = 0;
    memset(linker , -1 ,sizeof(linker));
    for(int i = 1; i<= n ;i ++){
        memset(vis, true, sizeof(vis));
        if(fin(i)){
            res ++ ;
        }
    }
    return res ;
}
int f[105];
int a[105];
int b[105];
int main(){
    while(scanf("%d",&n)!=EOF){
        for(int i = 1; i <= n ;i ++)q[i].clear();
        for(int i = 1; i <= n ;i ++){
            for(int j = 1; j<= n ;j ++){
                int x;
                scanf("%d", &x );
                if(x == 1)
                    q[i].push_back(j);
            }
        }
        int pd = xyl() ;
        if(pd != n){
            printf("%d\n", -1);
            continue;
        }
        for(int i = 1; i<= n ;i ++)f[i] = i ;
        int ans = 0;
        for(int i = 1; i<= n ;i ++){
            int z = linker[i];
            int where = f[z];
            if(where != i){
                ans ++ ;
                a[ans] = where ;
                b[ans] = i ;
                for(int j = 1; j<= n; j ++){
                    if(f[j] == i){
                        f[j] = where ;
                        f[z] = j ;
                    }
                }
            }
        }
        printf("%d\n",ans);
        for(int i = 1; i<= ans ;i ++){
            printf("R %d %d\n",a[i] , b[i]);
        }
    }
}

F hdu 2389

建图是容易想的 左边是人 右边是伞 最大匹配就可以了

问题在于 xyl算法的时间复杂度是VE 3000个点的情况下 稠密图会超时

在解决这类问题的时候 HK算法可以达到 E*sqrt(V)的时间复杂度

并且 在处理分层图的时候 网络流Dinic算法也可以达到E*sqrt(V)的复杂度

但是网络流也T掉了不知道为什么... 

HK算法是通过每一次bfs来看 当前有没有可以进行增广的潜力 并且进行一个lv的处理

如果有潜力的话 就使用这个lv的处理 来进行增广路运算

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define L long long
int n , m ;
int p ;
int x[3050];
int y[3050];
int s[3050];
int jl[3050];

vector<int >q[3050];
int linker[3050];
bool mx[3050];
int dx[3050] , dy[3050];
int dis ;
bool vis[3050];
int INF = 1 << 28 ;
bool bfs(){
    queue<int >que;
    dis = INF ;
    memset(dx, -1 , sizeof(dx));
    memset(dy, -1 , sizeof(dy));
    for(int i = 1; i <= n; i ++){
        if(mx[i] == false){
            que.push(i);
            dx[i] = 0;
        }
    }
    while(!que.empty()){
        int f = que.front(); que.pop ();
        if(dx[f] > dis )break;
        for(int i = 0; i < q[f].size() ;i ++ ){
            int v = q[f][i];
            if(dy[v] == -1 ){
                dy[v] = dx[f] + 1;
                if(linker[v] == -1)dis = dy[v];
                else {
                    dx[linker[v]] = dy[v] + 1 ;
                    que.push(linker[v]) ;
                }
            }
        }
    }
    if(dis == INF)return false ;
    else return true;
}
bool fin(int u ){
    for(int i = 0; i< q[u].size(); i ++ ){
        int v = q[u][i] ;
        if(vis[v] && dy[v] == dx[u] + 1){
            vis[v] = false;
            if(linker[v] != -1 && dis == dy[v])continue;
            if(linker[v] == -1 || fin(linker[v])){
                linker[v] = u ;
                mx[u] = true ;
                return true;
            }
        }
    }
    return false ;
}
int hk(){
    int res = 0;
    memset(mx , false , sizeof(mx)) ;
    memset(linker , -1 , sizeof(linker)) ;
    while(bfs()){
        memset(vis , true , sizeof(vis));
        for(int i = 1 ; i<= n ;i ++){
            if(mx[i] == false && fin(i)){
                res ++ ;
            }
        }
    }
    return res ;
}
bool ok[3050];
int main(){
    int t;
    scanf("%d",&t);
    int cas = 1;
    while(t -- ){
        memset(ok , false , sizeof(ok)) ;
        scanf("%d",&p);
        scanf("%d",&n);
        for(int i = 1; i<=n ;i ++)q[i].clear();
        for(int i = 1; i<=n ;i ++){
            scanf("%d%d%d",&x[i],&y[i],&s[i]);
            jl[i] = s[i] * s[i] * p * p ;
        }
        scanf("%d",&m);
        for(int i = 1; i<=m ;i ++){
            int xx , yy ;
            scanf("%d%d",&xx,&yy);
            for(int j = 1; j <= n ;j ++){
                int dis = (xx - x[j])*(xx - x[j]) + (yy - y[j])*(yy - y[j]);
                if(dis <= jl[j]){
                    q[j].push_back(i);
                    ok[j] = true;
                }
            }
        }
        int ans = hk() ;
        printf("Scenario #%d:\n",cas ++ );
        printf("%d\n\n",ans);
    }
}

G hdu4185

对于相连的两个油田连边 答案就是最大匹配

虽然点数达到了36w 边数最大也有72w左右 需要使用HK或者Dinic 

但是这道题数据很小 xyl就可以过了 .. 为什么我会知道这种事情..

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define L long long
int n ;
vector<int >q[360005];
int id[605][605];
char s[605][605];
int m ;
bool vis[360005];
int linker[360005];
bool fin(int u ){
    for(int i = 0 ; i< q[u].size(); i ++){
        int v = q[u][i];
        if(vis[v]){
            vis[v] = false;
            if(linker[v] == -1 || fin(linker[v])){
                linker[v] = u ;
                return true;
            }
        }
    }
    return false ;
}
int xyl(){
    int res = 0;
    memset(linker , -1 , sizeof(linker)) ;
    for(int i = 1 ; i<= m ;i ++){
        for(int j = 1; j<= m ;j ++)vis[j] = true;
        if(fin(i)){
            res ++ ;
        }
    }
    return res;
}
int main(){
    int t;
    scanf("%d",&t);
    int cas = 1;
    int cnt = 0;
    memset(id, 0 , sizeof(id)) ;
    while(t -- ){
        scanf("%d" ,&n );
        int minn = cnt ;
        for(int i = 1 ; i<= n ; i ++){
            scanf("%s", s[i] + 1);
        }
        for(int i = 1 ; i<= n ; i ++){
            for(int j = 1; j<= n ; j ++){
                if(s[i][j] == '#'){
                    id[i][j] = ++ cnt ;
                }
            }
        }
        m = cnt - minn ;
        for(int i = 1; i<= m ;i ++){
            q[i].clear() ;
        }
        for(int i = 1; i<= n ; i ++ ){
            for(int j = 1; j <= n ;j ++){
                if(id[i][j] > minn){
                    if(id[i+1][j] > minn){
                        int u = id[i][j] - minn ;
                        int v = id[i+1][j] - minn ;
                        q[u].push_back(v) ;
                        q[v].push_back(u) ;
                    }
                    if(id[i][j+1] > minn){
                        int u = id[i][j] - minn ;
                        int v = id[i][j+1] - minn ;
                        q[u].push_back(v) ;
                        q[v].push_back(u) ;
                    }
                }
            }
        }
        int ans = xyl();
        printf("Case %d: %d\n",cas ++ , ans/2);
    }
}

H hdu1054

求一下最小点覆盖就可以了 最小点覆盖 = 最大匹配

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define L long long
int n ;
vector<int >q[50000];
int linker[50000];
bool vis[50000];
bool fin(int u){
    for(int i = 0 ; i< q[u].size() ; i ++ ){
        int v = q[u][i];
        if(vis[v]){
            vis[v] = false;
            if(linker[v] == -1 || fin(linker[v])){
                linker[v] = u ;
                return true ;
            }
        }
    }
    return false ;
}
int xyl(){
    int res = 0;
    memset(linker, -1 ,sizeof(linker));
    for(int i = 0; i< n ;i ++){
        memset(vis, true , sizeof(vis));
        if(fin(i))res ++ ;
    }
    return res ;
}
int main(){
    while(scanf("%d",&n)!=EOF){
        for(int i = 0; i< n ;i ++)q[i].clear() ;
        for(int i = 1 ;i<= n ; i ++){
            int u ;
            int m ;
            scanf("%d:(%d)",&u , & m);
            for(int j = 1 ; j<= m ; j ++){
                int x;
                scanf("%d",&x);
                q[u].push_back(x);
                q[x].push_back(u);
            }
        }
        int ans = xyl();
        printf("%d\n",ans/2);
    }
}

J hdu1151 & K poj2594

题目所求 一个DAG的最小不相交路径 = DAG中的顶点 - 二分图的最大匹配

将最小相交路径转化为最小不相交路径的办法 : 如果a可以到达b 那么a到b有一条边 

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define L long long
int n ;
int m ;
vector<int >q[50000];
bool a[150][150];
int linker[150];
bool vis[150];
bool fin(int u){
    for(int i = 0; i < q[u].size() ;i ++ ){
        int v = q[u][i];
        if(vis[v]){
            vis[v] = false ;
            if(linker[v] == -1 || fin(linker[v])){
                linker[v] = u ;
                return true;
            }
        }
    }
    return false ;
}
int xyl(){
    int res = 0;
    memset(linker , -1 , sizeof(linker)) ;
    for(int i = 1; i <= n ; i ++ ){
        memset(vis , true ,sizeof(vis)) ;
        if(fin(i)){
            res ++ ;
        }
    }
    return res ;
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n, &m);
        for(int i = 1; i <= n ;i ++)q[i].clear();
        memset(a, false , sizeof(a));
        for(int i = 1; i <= m ; i ++ ){
            int u , v;
            scanf("%d%d",&u , &v );
            a[u][v] = true ;
        }
        for(int i = 1; i<= n ;i ++){
            for(int j = 1; j <= n; j ++){
                for(int k = 1; k <= n ;k ++){
                    if(a[i][k] == true && a[k][j] == true){
                        a[i][j] = true;
                    }
                }
            }
        }
        for(int i = 1; i<= n ;i ++) {
            for(int j = 1 ; j <= n ;j ++){
                if(a[i][j]){
                    q[i].push_back(j) ;
                }
            }
        }
        int maxx = xyl() ;
        int ans = n - maxx ;
        printf("%d\n",ans) ;
    }
}

L hdu3829

因为题目限定了一个人的hate与love的相反 那么可以将孩子分为两部分 一部分喜欢狗 一部分喜欢猫 

如果矛盾是边 那么满足二分图

求这个二分图的最大独立点集就可以了

最大独立点集 : 一个二分图中的点的集合 里面的所有的点相互之间没有边 

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define L long long
int n , m , p;
vector<int >q[505];
int linker[505];
bool vis[505];
struct node {
    char l ;
    int num1 ;
    char h ;
    int num2 ;
    int id ;
}a[505];

bool fin(int u){
    for(int i = 0; i< q[u].size() ;i ++ ){
        int v = q[u][i];
        if(vis[v]){
            vis[v] = false ;
            if(linker[v] == -1 || fin(linker[v])){
                linker[v] = u ;
                return true;
            }
        }
    }
    return false ;
}
int cntc ;
int cntd ;
int xyl(){
    int res = 0;
    memset(linker , -1 ,sizeof(linker)) ;
    for(int i = 1 ; i <= cntc ; i ++ ){
        memset(vis , true , sizeof(vis));
        if(fin(i))
            res ++ ;
    }
    return res ;
}
int main(){
    while(scanf("%d%d%d",&n,&m,&p)!=EOF){
        for(int i = 1; i<= p ; i ++)q[i].clear();
        cntc = 0;
        cntd = 0;
        for(int i = 1; i<= p ; i ++){
            char s[50];
            scanf("%s,",s);
            a[i].l = s[0] ;
            a[i].num1 = 0;
            int len = strlen(s);
            for(int j = 1 ; j < len ; j ++)a[i].num1 *= 10 , a[i].num1 += ( s[j] - '0' );
            scanf("%s,",s);
            a[i].h = s[0] ;
            a[i].num2 = 0;
            len = strlen(s);
            for(int j = 1 ; j < len ; j ++)a[i].num2 *= 10 , a[i].num2 += ( s[j] - '0' );
            if(a[i].l == 'C'){
                cntc ++ ;
                a[i].id = cntc;
            }
            else {
                cntd ++ ;
                a[i].id = cntd ;
            }
        }
        for(int i = 1; i <= p ; i ++ ){
            for(int j = 1 ; j <= p ; j ++ ){
                if((a[i].l == a[j].h && a[i].num1 == a[j].num2) || (a[i].h == a[j].l && a[i].num2 == a[j].num1)) {
                    if(a[i].l == 'C'){
                        q[a[i].id].push_back(a[j].id) ;
                    }
                    else {
                        q[a[j].id].push_back(a[i].id);
                    }
                }
            }
        }
        int ans = xyl() ;
        printf("%d\n", p - ans) ;
    }
}

经过几天的手速练习 发现二分图难在建模 代码是很套路的东西 交给队里最菜的主代码手就可以了 

所以 虽然我做了很多模板题 但是 我仍然训练了我在队里的担当 ... 

剩下的明天开始做吧 ...

转载于:https://www.cnblogs.com/rayrayrainrain/p/6260546.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值