@description@

有一天你学了一个能解决二分图最大权匹配的算法,你决定将这个算法应用到NOI比赛中。

给定一张完全二分图。在这张图里,两个部分的的大小均为 n。对于第一部分的点 u 和第二部分的点 v ,连接它们的边的权值为 \(c_{uv}+k_{uv}x\),其中 x 为一个值不确定的变量。

你将被多次给定 x 的值,对于每一个 x 的值,你需要回答对应的二分完全图的最大权匹配的总权值。

input

第一行一个数n,含义如题所述。

接下来 n 行,每行 n 个整数,其中第 i 行的第 j 个数为 cij 的值。

接下来nn行,每行nn个整数,其中第 i 行的第 j 个数为 kij 的值。

接下来一个数 q,表示给定的 x 的值的数量。

接下来 q 行,每行一个整数表示给定的 x 的值。

output

输出 q 行,每行一个数,其中第 i 行表示对应于第 i 个 x 的值的答案。

sample input

3

0 0 2

0 2 0

2 0 0

0 1 0

1 1 0

0 0 1

3

0

1

3

sample output

6

7

9

explanation

对于 x=0,最大匹配为 0→2,1→1,2→0,答案为 2+2+2=6。

对于 x=1,最大匹配仍然为 0→2,1→1,2→0,由于只有 \(k_{11}=1\)而另外两条边的 k 为零,只有第二条边的边权有变化,为 3。答案为2+3+2=7。

对于 x=3,最大匹配变为 0→1,1→0,2→2,因为这三条边的 k 值均为1,边权均变成了3。答案为 3+3+3=9。

对于100%的数据,1≤n≤50,1≤q≤100000,0≤cij≤10^7,0≤kij≤1,给定的 x 的值为在 0 到 10^7 之间的整数。

@solution@

不难发现答案一定形如 K*x + C 的形式。因为匹配最多 n 条边,所以 1<=K<=n。

进一步发现,每一个 K 唯一对应一个 C。这意味着答案的变化只会存在 n 种可能性。

我们只需要找出这 n 种可能性,再对于每一个询问找该询问对应哪一种可能性即可。

直观上可以发现(同时也不难使用反证法证明),当 x 越大时,最优解对应的 K 一定随之增大。

这意味着每一种 K 对应的 x 总是一段连续的区间,于是我们可以通过二分 x 的值找到每一种 K 对应的区间。

考虑求解最大权匹配时使用 KM 算法 O(n^3) 求解,我们的预处理时间复杂度 O(n^4*log(A)),其中 A = 10^7 是一个常数。

而询问只需要遍历 n 种可能性,故询问总时间复杂度为 O(nq)。

只要你的 KM 算法写的真的是 O(n^3)(可以去 uoj#80 测一测)而不是 O(n^4) 就可以过。

其实真正用得到的 x 只有 q 次询问中的 x。我们可以将询问排序后在询问上二分,就可以将时间复杂度将至 O(n^4*log(q))

但我懒得写这个优化。

@accepted code@

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int INF = 1<<30;
int c[50 + 5][50 + 5], k[50 + 5][50 + 5], K, X, C;
int f(int x, int y) {return k[x][y]*X + c[x][y];}
int lx[50 + 5], ly[50 + 5], lk[50 + 5], slk[50 + 5];
bool vx[50 + 5], vy[50 + 5];
int n, q;
bool dfs(int x) {
vx[x] = true;
for(int y=1;y<=n;y++) {
if( vy[y] ) continue;
int t = lx[x] + ly[y] - f(x, y);
if( t == 0 ) {
vy[y] = true;
if( (!lk[y]) || dfs(lk[y]) ) {
lk[y] = x;
return true;
}
}
else slk[y] = min(slk[y], t);
}
return false;
}
void KM(int p) {
X = p;
for(int i=1;i<=n;i++) {
lx[i] = ly[i] = lk[i] = 0;
for(int j=1;j<=n;j++)
lx[i] = max(lx[i], f(i, j));
}
for(int x=1;x<=n;x++) {
for(int i=1;i<=n;i++)
vx[i] = vy[i] = false, slk[i] = INF;
if( !dfs(x) ) {
while( true ) {
int del = INF, y = 0;
for(int i=1;i<=n;i++)
if( !vy[i] ) del = min(del, slk[i]);
for(int i=1;i<=n;i++) {
if( vx[i] ) lx[i] -= del;
if( vy[i] ) ly[i] += del;
}
for(int i=1;i<=n;i++)
if( !vy[i] ) {
slk[i] -= del;
if( slk[i] == 0 )
y = i;
}
if( !lk[y] ) break;
vx[lk[y]] = vy[y] = true;
for(int i=1;i<=n;i++)
slk[i] = min(slk[i], lx[lk[y]] + ly[i] - f(lk[y], i));
}
for(int i=1;i<=n;i++)
vx[i] = vy[i] = false;
dfs(x);
}
}
K = C = 0;
for(int i=1;i<=n;i++)
K += k[lk[i]][i], C += c[lk[i]][i];
}
struct node{
int k, c, l, r;
node(int _k=0, int _c=0, int _l=0, int _r=0):k(_k), c(_c), l(_l), r(_r){}
};
vector<node>vec;
int pk, pc;
int solve(int l, int r) {
while( l < r ) {
int mid = (l + r + 1) >> 1; KM(mid);
if( pk == K ) l = mid;
else r = mid - 1;
}
return l;
}
int main() {
scanf("%d", &n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d", &c[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d", &k[i][j]);
int le = 0, ri = int(1E7);
while( le <= ri ) {
KM(le); pk = K, pc = C;
int res = solve(le, ri);
vec.push_back(node(pk, pc, le, res));
le = res + 1;
}
scanf("%d", &q);
for(int i=1;i<=q;i++) {
int x; scanf("%d", &x);
for(int j=0;j<vec.size();j++)
if( vec[j].l <= x && x <= vec[j].r )
printf("%d\n", vec[j].k*x + vec[j].c);
}
}

@details@

本身来说这道题难度不大,只是需要想到一开始 K*x + C 中 K 的取值只有 n 种。

另外网上百度出来的 KM 算法大多都是假的 O(n^3)(包括百度百科),如果真的想找 O(n^3) 的代码可以去搜 uoj#80 的题解。

其实我也不知道把 KM 算法换成费用流能不能过。虽然费用流的复杂度是 O(玄学),不过完全图应该跑不快。。。

@noi.ac - 507@ 二分图最大权匹配的更多相关文章

  1. POJ2195 Going Home[费用流|二分图最大权匹配]

    Going Home Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 22088   Accepted: 11155 Desc ...

  2. Hdu2255 奔小康赚大钱(二分图最大权匹配KM算法)

    奔小康赚大钱 Problem Description 传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子. 这可是一件大事,关系到人民的住房问题啊.村里共有n间房间,刚好 ...

  3. [ACM] HDU 2255 奔小康赚大钱 (二分图最大权匹配,KM算法)

    奔小康赚大钱 Problem Description 传说在遥远的地方有一个很富裕的村落,有一天,村长决定进行制度改革:又一次分配房子. 这但是一件大事,关系到人民的住房问题啊. 村里共同拥有n间房间 ...

  4. POJ2195 Going Home (最小费最大流||二分图最大权匹配) 2017-02-12 12:14 131人阅读 评论(0) 收藏

    Going Home Description On a grid map there are n little men and n houses. In each unit time, every l ...

  5. HDU2255 奔小康赚大钱 —— 二分图最大权匹配 KM算法

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2255 奔小康赚大钱 Time Limit: 1000/1000 MS (Java/Others)    ...

  6. 二分图最大权匹配——KM算法

    前言 这东西虽然我早就学过了,但是最近才发现我以前学的是假的,心中感慨万千(雾),故作此篇. 简介 带权二分图:每条边都有权值的二分图 最大权匹配:使所选边权和最大的匹配 KM算法,全称Kuhn-Mu ...

  7. UOJ#80. 二分图最大权匹配 模板

    #80. 二分图最大权匹配 描述 提交 自定义测试 从前一个和谐的班级,有 nlnl 个是男生,有 nrnr 个是女生.编号分别为 1,…,nl1,…,nl 和 1,…,nr1,…,nr. 有若干个这 ...

  8. [SDOI2006] 仓库管理员的烦恼 - 二分图最大权匹配

    最小化代价,即最大化"本土"货物的数量 于是就是个二分图最大权匹配裸题 #include <bits/stdc++.h> using namespace std; #d ...

  9. [SDOI2017] 新生舞会 - 二分图最大权匹配,分数规划,二分答案

    有一个二分图,每个部都有 \(n\) 个点,每条边有两个参数 \(a_e, b_e\),求一种匹配,使得 \(\sum a_i / \sum b_i\) 最大 Solution 显然的分数规划,考虑二 ...

随机推荐

  1. JS中int和string的转换

    1.int型转换成string型 (1) var   x=100    a   =   x.toString()    (2) var   x=100;    a   =   x   +"& ...

  2. linux系统级别的计划任务及其扩展anacrontab

    这个是系统设置好了,清理系统垃圾或者是自动执行某些脚本的系统任务,一般我们做了解就行了,不要更改配置文件是/etc/conrtab SHELL:就是运行计划任务的解释器,默认是bash PATH:执行 ...

  3. js实现自由落体

    实现自由落体运动需要理解的几个简单属性: clientHeight:浏览器客户端整体高度 offsetHeight:对象(比如div)的高度 offsetTop:对象离客户端最顶端的距离 <!d ...

  4. PHP判断一个文件是否能够被打开

    <?php // 需求:因为系统涉及大量的文档知识库,用户可以在线进行查看.为了验证文档是否正常打开.先需要从数据库取出路径和文件名,判断是否可以从对应的路径下打开文件.header(" ...

  5. python通过http(multipart/form-data)上传文件的方法

    之前写过一篇博客,说的如何python如何通过http下载文件,今天写一篇博客来介绍如下,python如何通过request库实现上传文件 这里主要是解决multipart/form-data这种格式 ...

  6. const、引用与指针

    前提 我们忽略掉了相同类型是否可以赋值的情况(我到现在的学习里都还可以相互赋值),以及类型兼容的情况.只考虑const.&.*等修饰符带来的影响 类型兼容: 强制类型转换 基类与子类间的兼容 ...

  7. Keil新建工程步骤

    第一步:创建工程文件夹 1.新建一个文件夹,例如: 2.在文件夹下创建子文件夹: 文件夹说明: App:存放硬件控制程序: Libraries:存放固件库: Obj:存放生成的文件: Public:存 ...

  8. 怎么在 CentOS 6 上配置私有 NPM 仓库?

    Sinopia 是一个简单易用的私有 NPM 仓库服务器.在 CentOS 6 上安装时,遇到如下报错(Node 版本 6.9.1) #error This version of node/NAN/v ...

  9. 【风马一族_php】NO5_php基础知识_数组

    原文来自:http://www.cnblogs.com/sows/p/6032570.html (博客园的)风马一族 侵犯版本,后果自负 回顾 匿名函数 定义: 变量 = function [参数列表 ...

  10. golang的包导入import

    import别名/点下划线(1)import关键字的作用:作用是导入该go源文件所依赖的package包.用在go源文件中,紧接在pakage后面的部分.(2)只要在源文件中用到了的package包就 ...