2018年2月28日训练日记

昨晚趁着精神好看资料到三点多,中间发现一道水题就交了一发,居然还submit failed。。。二分图最大匹配最优匹配会了之后感觉很简单,最难的地方还是建图。今天醒来又交了一遍WA,找了半天发现这道题可能有重边。。。(已经在重边这里错了若干次了)

E题swap就是一个二分图最大匹配,可是我怎么都过不了样例,郁闷了我两三天,后来才发现输出任意答案都行,而且根据矩阵性质,如果行交换不能达到条件的话,列交换或行列交换也不可能达到条件。并且它这个交换还是一步交换,即某两行交换完了就不可以再交换。这道题还是有必要写一下题解的。。。(好不容易整出思路,一个小细节错了又害我找了半天)

今天看一般图匹配带花树二分图多重匹配

貌似二分图多重匹配可以用网络流做。。。赶紧去复习了一下网络流

感觉网络流还是写起来比较麻烦。。。而且为了熟悉二分图多重匹配,就不用网络流做了,以后用网络流再做一遍

先学习完网络流以后,建图就比较好想了。。。于是先去做专题里的题,题目用到二分或floyed也跟网络流里的差不多。。。

专题里三道二分图多重匹配题都用到了二分,也是比较经典的二分,有一道还用到了floyed。

需要注意几点:

1、n和m不能搞混 否则就 WA RE

2、注意初始化 数组及时清零 注意编号 注意是否有重边

3、题意一定要弄清楚,n,m,k都是什么含义,否则我就不说了,都懂

4、用floyed求最短路时一定要把距离0置为inf,因为0会干扰求最短路

5、二分图多重匹配题目用网络流也可以做,不过实现起来比较麻烦,但是两种方式大同小异

贴上poj 2112 Optimal Milking (floyed + 二分图多重匹配/网络流 + 二分)的个人代码:

#include<iostream>
#include<cmath>
#include<iomanip>
#include<map>
#include<vector>
#include<algorithm>
#include<queue>
#include<string>
#include<cstdio>
#include<cstring>
#include<set>
using namespace std;
const int maxn=610;
const int maxm=610;
const int inf=1e9;
const int mo=1e9+7;
int n,m,lk[maxn][maxm],ans,k;
int g[maxm][maxn],num[maxm];
int c[maxn][maxm];
bool jd[maxn][maxm],us[maxm];
bool dfs(int u)
{
   for(int v=1;v<=n;v++)
        if(jd[u][v]&&!us[v])
        {
            us[v]=1;
            if(g[v][0]<k)
            {
             g[v][++g[v][0]]=u;
             return true;
            }
            for(int i=1;i<=g[v][0];i++)
            if(dfs(g[v][i]))
            {
                g[v][i]=u;
                return 1;
            }
        }
   return 0;
}
bool solve(int mid)
{
 for(int i=1;i<=n;i++) g[i][0]=0;//注意n m
 for(int u=1;u<=m;u++)
 {
  memset(us,0,sizeof(us));
  if(!dfs(u))return 0;
 }
 return 1;
}
bool jud(int mid)
{
 memset(jd,0,sizeof(jd));
 for(int i=n+1;i<=n+m;i++)
 for(int j=1;j<=n;j++)
 {
     if(c[i][j]<=mid) jd[i-n][j]=1;//注意编号
 }
 return solve(mid);
}
int main()
{
    char s[20],ch;
    int v;
    while(scanf("%d%d%d",&n,&m,&k)!=EOF)
    {
        //memset(jd,0,sizeof(jd));
        int r=inf;
        for(int i=1;i<=n+m;i++)
        for(int j=1;j<=n+m;j++)
        {
        scanf("%d",&c[i][j]);
        if(!c[i][j]) c[i][j]=inf;//距离为0置为inf
        }
        for(int kk=1;kk<=n+m;kk++)
        for(int i=1;i<=n+m;i++)
        for(int j=1;j<=n+m;j++)
        if(c[i][kk]+c[kk][j]<c[i][j])c[i][j]=c[i][kk]+c[kk][j];
        int l=1;
        while(l<=r)//二分
        {
         int mid=((l+r)>>1);
         if(jud(mid)) {r=mid-1;}
         else l=mid+1;
        }
        printf("%d\n",l);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值