二分匹配

/*
题意: 一家维修公司,服务的 Q 街区。每个街区有一定距离, 一天公司收到M修理任务,第i个任务发生在第i个街区,并且每个任务都有起始时间 ti,完成一个任务需要di时间.修理工必须完成一个任务后,再去另一个.问要完成这M个任务最少需要多少个修理工?

思路:有向图的最小路径覆盖。先用floyd求出两点间的最短距离,然后建图 若第j个街区任务的开始时间大于等于第i个街区的任务完成时间 + 路上花费时间 则i到j间连一条边 建立二分图 求最大匹配 。ans = 顶点数 - 最大匹配数。

POJ  3216


*/

#include<cstdio>
#include<cstring>
using namespace std;
const int INF=0x3f3f3f3f;
int q,m;
int dict[30][30],g[220][220],linker[220],vis[220];
struct Edge{
    int id;
    int x,y;
}task[220];
//最大匹配算法模板
int DFS(int u){
    for(int i=1;i<=m;i++){
        if(g[u][i]&&!vis[i]){
            vis[i]=1;
            if(linker[i]==-1||DFS(linker[i])){
                linker[i]=u;
                return 1;
            }
        }
    }
    return 0;
}
int Hungary(){
    int ans=0;
    memset(linker,-1,sizeof(linker));
    for(int i=1;i<=m;i++){
        memset(vis,0,sizeof(vis));
        if(DFS(i))
            ans++;
    }
    return ans;
}
int main(){
    while(~scanf("%d%d",&q,&m)){
        if(q==0&&m==0)
            break;
        memset(g,0,sizeof(g));
        for(int i=1;i<=q;i++)
            for(int j=1;j<=q;j++){
                scanf("%d",&dict[i][j]);
                if(dict[i][j]==-1)
                    dict[i][j]=INF;
        }
        int d;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&task[i].id,&task[i].x,&d);
            task[i].y=task[i].x+d;
        }
        //floyd算法
        for(int k=1;k<=q;k++)
            for(int i=1;i<=q;i++)
                for(int j=1;j<=q;j++)
                   if(dict[i][j]>dict[i][k]+dict[k][j])
                        dict[i][j]=dict[i][k]+dict[k][j];
        //构造二分匹配关系图
        for(int i=1;i<=m;i++)
            for(int j=1;j<=m;j++)
                if(task[j].x>=task[i].y+dict[task[i].id][task[j].id])
                    g[i][j]=1;
        int ans=Hungary();
        printf("%d\n",m-ans);
        }
}


/*
题意:有A,B两台机器, 机器A 有 n个模式(0, 1, 2....n-1),同样机器B有m个模式, 两个机器一开始的模式都为0,有k个作业(id,x,y) 表示作业编号id, 该作业必须在A机器在模式x下或者B机器在模式y下完成,问你至少要切换几次机器模式。
思路:问题就是问用最少的模式完成所有的任务,每一种模式是一个点,A为一个集合,B为一个集合建立二分图。每一条边就代表这一个任务,现在要用最少的点覆盖所有的边,几最小点集覆盖。
POJ  1325
*/

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 110
int pre[maxn],vis[maxn],dict[maxn][maxn];
bool dfs(int u,int n){
    for(int i=1;i<=n;i++){
        if(!vis[i]&&dict[u][i]){
            vis[i]=1;
            if(pre[i]==-1||dfs(pre[i],n)){
                pre[i]=u;
                return true;
            }
        }
    }
    return false;
}
int main(){
    int na,nb,t;
    while(scanf("%d",&na)&&na){
        memset(dict,0,sizeof(dict));
        memset(pre,-1,sizeof(pre));
        scanf("%d%d",&nb,&t);
        while(t--){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            dict[y][z]=1;
        }
        int sum=0,ma=max(na,nb);
        for(int i=1;i<=na;i++){
            memset(vis,0,sizeof(vis));
            if(dfs(i,ma))
                sum++;
        }
        printf("%d\n",sum);
    }
}

/*
题意:在一个有向无环图中,从一些顶点出发,能遍历到图上所有点,要求初始选择的顶点数最少且顶点不重复遍历。

思路:
如果从某个顶点开始遍历的过程看成是路径的选择,那么问题就转化为在有向无环图中找最少的不想交的简单路径,这些路径覆盖图中的所有顶点。可见是关于最小路径覆盖的问题。

在有向无环图中,最小路径覆盖数  = 节点数 — 其对应二分图的最大匹配数。
最小路径覆盖它要求原图必须是有向无环图。

POJ  1422


*/

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int MAXN =305;
int dict[MAXN][MAXN];
int cx[MAXN],linker[MAXN],n,m;
bool visited[MAXN];
int dfs(int u){
    for(int i=1;i<=n;i++){
        if(dict[u][i]&&!visited[i]){
            visited[i]=true;
            if(linker[i]==-1||dfs(linker[i])){
                linker[i]=u;
                return true;
            }
        }
    }
    return false;
}
int main(){
    int test;
    scanf("%d",&test);
    while(test--){
        int x,y;
        memset(dict,false,sizeof(dict));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            scanf("%d%d",&x,&y);
            dict[x][y]=true;
        }
        int sum=0;
        memset(linker,-1,sizeof(linker));
        for(int i=1;i<=n;i++){
            memset(visited,0,sizeof(visited));
            if(dfs(i))
                sum+=1;
        }
        printf("%d\n",n-sum);
    }
}



/*
POJ  1469
模板题

*/

#include<cstdio>
#include<cstring>
#define N 1000
using namespace std;
int n,p,linker[N],sum;
bool dict[N][N],visited[N];
bool dfs(int a){
    for(int i=1;i<=n;i++)
    if(dict[a][i]&&!visited[i]){
        visited[i]=1;
        if(linker[i]==-1||dfs(linker[i])){
            linker[i]=a;
            return true;
        }
    }
    return false;
}
int main(){
    int test,counti,temp;
    scanf("%d",&test);
    while(test--){
        scanf("%d%d",&p,&n);
        sum=0;
        memset(dict,false,sizeof(dict));
        memset(linker,-1,sizeof(linker));
        for(int i=1;i<=p;i++){
            scanf("%d",&counti);
            for(int j=1;j<=counti;j++){
                scanf("%d",&temp);
                dict[i][temp]=true;
            }

        }
        for(int i=1;i<=p;i++){
            memset(visited,0,sizeof(visited));
            if(dfs(i))
                sum++;
        }
        if(sum==p)
            printf("YES\n");
        else
            printf("NO\n");
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值