KM bfs写法
KM bfs写法
2018astar资格赛的第三题整数规划。
把\(x, y\)看成二分图两边的顶标,\(a_{ij}\)就是二分图的边权,整道题其实就是求二分图的最大权匹配。
然后打了个\(dfs\)的\(KM\),\(TLE\)了,后来听别人说要用\(bfs\)的写法,因为那个才是真正的\(O(n^3)\),\(dfs\)的写法最坏情况还是\(O(n^4)\)。
原理是一样的,只不过\(bfs\)有一点点像迭代,每一次也只是搜\(diff=0\)的情况,而且右边的点只会搜索一次(或者说是左边的点只会搜索一次,即左边的每个点只会进队一次),用\(pre\)记住当前的交错路径,找到未匹配的就可以沿交错路径进行修改。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=210;
const LL inf=1LL<<60;
int n;
namespace KM
{
int n;
LL mat[maxn][maxn]; //边权
int matcha[maxn], matchb[maxn]; //左边的点匹配的右边点;右边的点匹配的左边点
LL marka[maxn], markb[maxn]; //左顶标;右顶标
LL slack[maxn]; //松弛数组
bool visa[maxn], visb[maxn]; //访问标记
int head, tail;
int q[maxn], pre[maxn]; //队列;交错路径
bool check(int cur)
{
visb[cur]=true; //标记cur已搜索
if (matchb[cur]) //已匹配,即当前匹配失败
{
if (!visa[matchb[cur]]) //匹配的点是否已进队
{
q[++tail]=matchb[cur];
visa[matchb[cur]]=true;
}
return false;
}
//未匹配,即当前匹配成功,沿交错路径进行匹配
while (cur)
swap(cur, matcha[matchb[cur]=pre[cur]]);
return true;
}
void bfs(int start)
{
fill(visa, visa+1+n, false);
fill(visb, visb+1+n, false);
fill(slack, slack+1+n, inf);
head=tail=1;
q[1]=start;
visa[start]=true;
while (1)
{
while (head<=tail)
{
int cur=q[head++];
for (int i=1; i<=n; ++i)
{
LL diff=marka[cur]+markb[i]-mat[cur][i];
if (!visb[i] && diff<=slack[i]) //visb=true说明已搜索,无需更新slack和pre,也是保证pre的正确性
{
slack[i]=diff;
pre[i]=cur;
if (diff==0) //diff=0,可以尝试匹配
if (check(i)) return; //匹配成功可直接返回
}
}
}
LL delta=inf;
for (int i=1; i<=n; ++i)
if (!visb[i] && slack[i]) delta=min(slack[i], delta);
for (int i=1; i<=n; ++i) //松弛
{
if (visa[i]) marka[i]-=delta;
if (visb[i]) markb[i]+=delta;
else slack[i]-=delta; //维护slack的正确性(参考diff的计算及marka,markb的变化)
}
head=1, tail=0;
for (int i=1; i<=n; ++i)
if (!visb[i] && !slack[i] && check(i)) return;
//松弛后尝试匹配diff=0的点。
}
}
void solve()
{
fill(matcha, matcha+1+n, 0);
fill(matchb, matchb+1+n, 0);
fill(markb, markb+1+n, 0);
for (int i=1; i<=n; ++i)
{
marka[i]=0;
for (int j=1; j<=n; ++j)
marka[i]=max(marka[i], mat[i][j]);
}
for (int i=1; i<=n; ++i) bfs(i);
}
}
void read()
{
scanf("%d", &n);
KM::n=n;
for (int i=1; i<=n; ++i)
for (int j=1; j<=n; ++j)
{
int x;
scanf("%d", &x);
KM::mat[i][j]=-x;
}
}
void solve()
{
KM::solve();
LL ans=0;
for (int i=1; i<=n; ++i)
ans+=KM::marka[i]+KM::markb[i];
printf("%lld\n", -ans);
}
int main()
{
int casesum;
scanf("%d", &casesum);
for (int i=1; i<=casesum; ++i)
{
printf("Case #%d: ", i);
read();
solve();
}
return 0;
}
KM bfs写法的更多相关文章
- POJ1915Knight Moves(单向BFS + 双向BFS)
题目链接 单向bfs就是水题 #include <iostream> #include <cstring> #include <cstdio> #include & ...
- Addition Chains POJ - 2248 (bfs / dfs / 迭代加深)
An addition chain for n is an integer sequence <a0, a1,a2,...,am=""> with the follow ...
- uoj#80 二分图最大权匹配
题意:给定二分图,有边权,求最大边权匹配.边权非负. 解:KM算法求解最大权完备匹配. 完备匹配就是点数少的那一边每个点都有匹配. 为了让完备匹配与最大权匹配等价,我们添加若干条0边使之成为完全二分图 ...
- 【bzoj1060】[ZJOI2007]时态同步
题目描述 小Q在电子工艺实习课上学习焊接电路板.一块电路板由若干个元件组成,我们不妨称之为节点,并将其用数字1,2,3-.进行标号.电路板的各个节点由若干不相交的导线相连接,且对于电路板的任何两个节点 ...
- APIO2017商旅
传送门(PDF) 题目大意:有$N$个点,$M$条有向边,$K$种物品,在不同的点可以用不同的价格买入或卖出某一种商品. 任意时刻至多持有一种物品,不能在同一个点先买再卖,求收益与长度之比最大的点数$ ...
- [LeetCode] 864. Shortest Path to Get All Keys 获得所有钥匙的最短路径
We are given a 2-dimensional grid. "." is an empty cell, "#" is a wall, "@& ...
- 【剑指Offer】60、按之字形顺序打印二叉树
题目描述 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推. 题解:BFS 主要的方法与BFS写法没什么区 ...
- bzoj3436小K的农场
bzoj3436小K的农场 题意: n个数,知道m条关系:a-b≥c.a-b≤c或a==b.问是否存在满足所有关系的情况.n≤10000,m≤10000. 题解: 差分约束.因为只要求是否满足,因此最 ...
- 【LeetCode】代码模板,刷题必会
目录 二分查找 排序的写法 BFS的写法 DFS的写法 回溯法 树 递归 迭代 前序遍历 中序遍历 后序遍历 构建完全二叉树 并查集 前缀树 图遍历 Dijkstra算法 Floyd-Warshall ...
随机推荐
- 【服务器_Tomcat】Tomcat的Server Options选项
一.配置 默认前两个是没有勾选的,应该勾选上: 在Cotext节点中有一个reloadable='true'属性,将它改为false,可以在修改java文件后不用重启服务器即可生效,但是不包括新建ja ...
- HDU1232——畅通工程
#include<stdio.h> ]; int find(int x) //查找根节点 { int r=x; while (pre[r]!=r) //返回根节点 r r=pre[r]; ...
- 转载--------Python中:self和__init__的含义 + 为何要有self和__init__
背景 回复:我写的一些Python教程,需要的可以看看,中SongShouJiong的提问: Python中的self,__init__的含义是啥?为何要有self,__init这些东西? 解释之前, ...
- 洛谷 P1858 多人背包 解题报告
P1858 多人背包 题目描述 求01背包前k优解的价值和 输入输出格式 输入格式: 第一行三个数\(K\).\(V\).\(N\) 接下来每行两个数,表示体积和价值 输出格式: 前k优解的价值和 说 ...
- CF785D Anton and School - 2 解题报告
CF785D Anton and School - 2 题意:给定一个长度\(\le 2 \times 10e5\)由'('和')'组成的字符串,问有多少个子串(可以不连续),前半部分是由\('('\ ...
- C中 ->运算符说明
岁数大了,记忆力不好!这里记下,以后忘了来查! ->运算符. 访问结构中的成员 用 点“.”运算符 Ex: typedef struct st { char a; int b; } st; 定义 ...
- LOJ分块⑨题汇总
从零开始的分块学习系列(感谢hzwer) 题目顺序是我建议的做题顺序 先说一句:分块的核心思想(其实本身分块就可以说是一种思想)是:均摊(或者说平衡/权衡?)复杂度,同时这种思想本身不只局限于序列分块 ...
- USB驱动之CDC类的介绍与应用20160905
USB的协议其实是很复杂的,如果要深入学习估计要一两年才能熟悉透.本文主要是讲如何使用官方已经写好的库进行二次开发,以达到我们自己使用的目的.我们知道USB可以用来接U盘,声卡,读卡器,鼠标键盘等等, ...
- laravel5.1 关联模型保存的方法(使用associate方法)
模型定义 class User { public function customer() { return $this->hasOne('Customer'); } } class Custom ...
- navicat for mysql 导出数据的坑
navicat 选择转储结构和数据的时候,生成的 sql 文件会比较大,因为每一条数据都会生成一条 sql 语句,所以会导致 使用 source 还原的时候会很慢很慢很慢, 而使用 mysqldump ...