【玲珑杯 1047】【二分匹配 KM算法或者费用流】Best couple【定义男女生的距离为最短距离,求匹配之后使得总距离最大】

来源:互联网 发布:java 变量类型 编辑:程序博客网 时间:2024/06/05 06:29

传送门:http://www.ifrog.cc/acm/problem/1055

思路:

跑一发floyd,然后再用km。

但是问题来了,这个有可能n != m。那怎么办?

其实可以补上一些不存在的点。来使得n = m。他们的权值就设置为0就好了。意思就是这些人的搭配,是对答案没有贡献的。注意不能设置为-inf。因为补上的那些点也是必须要选人的,只不过他们选了人,相当于没选而已(权值不存在。)如果设置为-inf的那些,那么他们就会把答案改了。


还有一个小trick的就是,一开始,我是把本来地图上的-1的那些点,更改为inf的,inf表示不连通,那么直接floyd就可以了不用特判那么多。那么问题又来了。如果跑了floyd后,还是不连通,那怎么办?他们的权值可是inf啊。组成新图的时候,同时也是需要把他们的权值设置为0的,也就是相当于没选。


或者直接跑费用流省去建虚点的麻烦


下面给出一些概念:

1.通俗地说,完全匹配只要一边的点都在匹配中就行。而完美匹配则是相当于要“双向的完全匹配”。
2.KM算法求的是完美匹配中权最大的一个,在本来图就达不到完美匹配,而只有若干完备匹配的情况下,KM不能求出完备匹配中权最大的一个。
3.如果本来图就达不到完美匹配,但仍要求出最大权匹配,那么该怎么办?
(1)建虚点虚边,只要把所有没有的边设成0就行了,然后跑KM算法
(2)用费用流来跑,这种情况用费用来跑相对来说代码少点,但是效率比KM稍慢

代码一:

(费用流 266ms)

#include <bits/stdc++.h>using  namespace  std;//最小费用最大流,求最大费用只需要取相反数结果即可。//点的总数为 N,点的编号 0~N const int maxn=205;const int maxm=100005;const int inf=0x3f3f3f3f;struct Edge{  int to,next,cap,flow,cost;}es[maxm];int head[maxn],tol;int p[maxn];//记录增广路径上 到达点i的边的编号 int d[maxn];//单位总费用bool vis[maxn];int N;//节点总个数int dp[maxn][maxn];int n, m;void init(int n){  N=n;  tol=0;  memset(head, -1, sizeof(head));}void addedge(int u,int v,int cap,int cost){  es[tol].to=v;  es[tol].cap=cap;  es[tol].cost=cost;  es[tol].flow=0;  es[tol].next=head[u];  head[u]=tol++;  es[tol].to=u;  es[tol].cap=0;  es[tol].cost=-cost;  es[tol].flow=0;  es[tol].next=head[v];  head[v]=tol++;}bool spfa(int s,int t){//寻找花销最少的路径   //跑一遍SPFA 找s——t的最少花销路径 且该路径上每一条边不能满流     //若存在 说明可以继续增广,反之不能    queue<int>q;  for(int i=0; i<=N; i++){    d[i]=inf;    vis[i]=false;    p[i]=-1;  }  d[s]=0;  vis[s]=true;  q.push(s);  while(!q.empty()){    int u=q.front();    q.pop();    vis[u]=false;    for(int i=head[u]; i!=-1; i=es[i].next){ //逆向枚举以u为起点的边      int v=es[i].to;      if(es[i].cap>es[i].flow && d[v]>d[u]+es[i].cost){//可以松弛 且 没有满流         d[v]=d[u]+es[i].cost;        p[v]=i;   //记录前驱边 的编号         if(!vis[v]){ vis[v]=true; q.push(v);}      }    }  }  return p[t]!=-1;//可达返回true}//返回的是最大流,cost存的是最小费用int MCMF(int s,int t,int &cost){  int flow = 0;cost = 0;  while(spfa(s,t)){//每次寻找花销最小的路径     int Min=inf;    //通过反向弧 在源点到汇点的最少花费路径 找最小增广流      for(int i=p[t]; i!=-1; i=p[es[i^1].to]){      if(Min>es[i].cap-es[i].flow)      Min=es[i].cap-es[i].flow;    }    //增广    for(int i=p[t]; i!=-1; i=p[es[i^1].to]){      es[i].flow+=Min;      es[i^1].flow-=Min;      cost+=es[i].cost*Min;//增广流的花销     }    flow+=Min;//总流量累加   }  return flow;}void floyd(){    for(int k=0; k<n+m; k++)        for(int i=0; i<n+m; i++)            for(int j=0; j<n+m; j++)                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);}int  main(){    int T;    scanf("%d", &T);    while(T--){        scanf("%d%d", &n, &m);        for(int i=0; i<n+m; i++)            for(int j=0; j<n+m; j++){                scanf("%d", &dp[i][j]);                if(dp[i][j] == -1)dp[i][j] = inf;            }        floyd();        init(n+m+2);        for(int i=0; i<n; i++)            addedge(n+m, i, 1, 0);//源点与男生之间建边        for(int i=n; i<n+m; i++)            addedge(i, n+m+1, 1, 0);//汇点与女生之间建边        for(int i=0; i<n; i++)            for(int j=n; j<n+m; j++)                if(dp[i][j] < inf)                    addedge(i, j, 1, -dp[i][j]);//男生与女生之间距离小于inf建边        int cost;        MCMF(n+m, n+m+1, cost);        printf("%d\n", -cost);    }      return 0;}


代码二:

(KM 126ms)

#include <bits/stdc++.h>using  namespace  std;#define mst(ss,b) memset(ss,b,sizeof(ss));/* KM算法* 复杂度O(nx*nx*ny)* 求最大权匹配* 若求最小权匹配,可将权值取相反数,结果取相反数* 点的编号从1开始*/const int N=105;const int inf=0x3f3f3f3f;int nx,ny;//两边的点数int g[N][N];//二分图描述int link[N],lx[N],ly[N];//y中各点匹配状态,x,y中的顶点标号int slack[N];bool visx[N],visy[N];int dp[2*N][2*N];int n, m;bool dfs(int x){  visx[x]=true;  for(int y=1; y<=ny; y++){    if(visy[y])continue;    int tmp=lx[x]+ly[y]-g[x][y];    if(tmp==0){      visy[y]=true;      if(link[y]==-1 || dfs(link[y])){        link[y]=x;        return true;      }    }    else if(slack[y]>tmp) slack[y]=tmp;  }  return false;}int KM(){  memset(link, -1, sizeof(link));  memset(ly, 0, sizeof(ly));  for(int i=1; i<=nx; i++){    lx[i]=-inf;    for(int j=1; j<=ny; j++)      if(g[i][j]>lx[i])        lx[i]=g[i][j];  }  for(int x=1; x<=nx; x++){    for(int i=1; i<=ny; i++) slack[i]=inf;    while(true){      memset(visx, false, sizeof(visx));      memset(visy, false, sizeof(visy));      if(dfs(x))break;      int d=inf;      for(int i=1; i<=ny; i++)        if(!visy[i] && d>slack[i])          d=slack[i];      for(int i=1; i<=nx; i++)        if(visx[i])          lx[i]-=d;      for(int i=1; i<=ny; i++){        if(visy[i])ly[i]+=d;        else slack[i]-=d;      }    }  }  int res=0;  for(int i=1; i<=ny; i++)  if(link[i]!=-1)    res+=g[link[i]][i];  return res;}void floyd(){    for(int k=0; k<n+m; k++)        for(int i=0; i<n+m; i++)            for(int j=0; j<n+m; j++)                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);}int  main(){    int T;    scanf("%d", &T);    while(T--){        mst(dp, 0);        scanf("%d%d", &n, &m);        for(int i=0; i<n+m; i++)            for(int j=0; j<n+m; j++){                scanf("%d", &dp[i][j]);                if(dp[i][j] == -1)dp[i][j] = inf;            }        floyd();        for(int i=1; i<=n; i++)                for(int j=1; j<=m; j++){                    g[i][j]=dp[i-1][n+j-1];                    if(g[i][j] >= inf)g[i][j]=0;                }        if(n < m){            for(int i=n+1; i<=m; i++)                for(int j=1; j<=m; j++)                    g[i][j]=0;        }        else if(n > m){            for(int i=1; i<=n; i++)                for(int j=m+1; j<=n; j++)                    g[i][j]=0;        }        nx = ny = max(n, m);              printf("%d\n", KM());    }      return 0;}




0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 脸上有闭口痘痘怎么办 泰迪得了皮肤癣怎么办 qq截图发不出去怎么办 半夜2点肚子饿怎么办 半夜两三点饿了怎么办 科目三预约不上怎么办 心脏支架又堵了怎么办 车辆验车过期了怎么办 车辆年审过期2天怎么办 违停单子丢了怎么办 沙发弹簧包坏了怎么办 bt种子被和谐了怎么办 苹果手机id密码忘了怎么办 墙漆颜色太深了怎么办 油漆颜色太深了怎么办 墙面漆颜色太深怎么办 古代打仗牙掉了怎么办 乐视电视没声音怎么办 老公疑心病很重怎么办啊 被安装了尿道锁怎么办 狼青小狗腿罗圈怎么办 备孕期间有霉菌怎么办 虫子进皮肤里了怎么办 生完孩子肚子越来越大怎么办 怀孕8个月肚子小怎么办 孕晚期胎儿不长怎么办 肚子上肉特别多怎么办 奶堵了有硬块怎么办 便秘5天肚子胀怎么办 上火大便拉不出来怎么办 大便拉不出来肚子痛怎么办 戒奶奶涨有硬块怎么办 忌奶的时候涨奶怎么办 娃儿隔奶,,奶涨怎么办 狗肚子很大很鼓怎么办 注册不了的二建怎么办 专升本没考过怎么办 警察乱拘留人该怎么办 玩英雄联盟太卡怎么办 一方离婚证丢了怎么办 遭遇呼死你软件怎么办