uoj#80 二分图最大权匹配

题意:给定二分图,有边权,求最大边权匹配。边权非负。

解:KM算法求解最大权完备匹配。

完备匹配就是点数少的那一边每个点都有匹配。

为了让完备匹配与最大权匹配等价,我们添加若干条0边使之成为完全二分图(自造名词别在意......)

为了让左边成为点数较少的一边,我们还要添加一些虚点,m = max(n,m)

然后求解完备匹配。

KM的DFS写法会被卡成n4,如果你不在意可以写......反正在uoj上会被卡爆。

模板就不放了,反正没啥用,放了还我 误 我 自 己。

过程就是一次为每个点寻找匹配。首先每个点都有个顶标(期望匹配值),最后要使得每一条匹配边(x, y)满足w[x] + w[y] = val(x, y)

然后每个右边的点还有个need数组,一般是叫做slack,就是松弛量,也就是当前这个点最少要把w减去多少才能匹配上。

还有个D是min slack,也就是全局最少减少D才能得到匹配。

匹配的时候跟匈牙利一样DFS,注意到一个点之后如果w[x] + w[y] = val(x, y)则这条边可用,打上vis,否则用差值更新need y

如果没有匹配就update一遍,每个有vis的左边减去D,右边加上D。然后继续直到有匹配为止。

BFS写法

这个我不太懂...话说DFS本来就不懂了,还纠结这个干啥。

右边节点有个pre数组表示它是谁更新来的,也就是如果它进入增广路,那么它前面的右边节点是pre

在BFS函数里首先设置BFS起点是右边0匹配左边x,然后尝试为x找到增广路。

枚举每个未被vis的y,得到w[x] + w[y]与val(x, y)的差值。

用这个差值更新need[y],如果差值比need[y]小,就表明y的前一个节点是mat[x]的话会更佳,令pre[y] = u(u表示正在匹配x的节点,也就是上一个x准备匹配的节点)

用need[y]更新D,如果need[y] < D则表明全局上下一个点选择y更优,令nex = y(此处nex是下一轮的u)

然后更新一遍,让所有vis的右边节点(当然也包括一开始我们虚拟跟x匹配的右边0号点)w += D,而与之匹配的左边节点w -= D

结束条件是找到的这个u没有匹配。此时增广路就找到了。

然后把交错路取反,也就是每个u的匹配变成上一个u的匹配,直到倒数第二个u匹配x。

啊我到底在口胡些什么

 1 //thanks to yyb
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5
 6 typedef long long LL;
 7 const int N = 410;
 8 const LL INF = 0x3f3f3f3f3f3f3f3f;
 9
10 int vis[N * 2], mat[N * 2], Time, pre[N * 2], n, m;
11 LL val[N][N], w[N * 2], need[N * 2];
12
13 void BFS(int x) {
14     memset(need, 0x3f, sizeof(need));
15     memset(pre, 0, sizeof(pre));
16     int u = 0, nex;
17     mat[u] = x;
18     do {
19         x = mat[u];
20         LL D = INF;
21         vis[u] = Time;
22         for(int y = n + 1; y <= n + m; y++) {
23             if(vis[y] == Time) {
24                 continue;
25             }
26             LL t = w[x] + w[y] - val[x][y - n];
27             if(t < need[y]) { // update need pre
28                 need[y] = t;
29                 pre[y] = u;
30             }
31             if(need[y] < D) { // update D nex
32                 D = need[y];
33                 nex = y;
34             }
35         }
36         // update
37         w[mat[0]] -= D; // do not forget!
38         w[0] += D;
39         for(int i = n + 1; i <= n + m; i++) {
40             if(vis[i] == Time) {
41                 w[mat[i]] -= D;
42                 w[i] += D;
43             }
44             else {
45                 need[i] -= D;
46             }
47         }
48         u = nex;
49     } while(mat[u]);
50
51     while(u) { // update path
52         mat[u] = mat[pre[u]];
53         u = pre[u];
54     }
55
56     return;
57 }
58
59 int main() {
60     int q;
61     scanf("%d%d%d", &n, &m, &q);
62     m = std::max(n, m);
63     for(int i = 1; i <= q; i++) {
64         int x, y;
65         LL z;
66         scanf("%d%d%lld", &x, &y, &z);
67         val[x][y] = std::max(val[x][y], z);
68         w[x] = std::max(w[x], val[x][y]);
69     }
70     for(int i = 1; i <= n; i++) {
71         ++Time; // ++Time
72         BFS(i);
73     }
74     LL ans = 0;
75     for(int i = n + 1; i <= n + m; i++) {
76         if(val[mat[i]][i - n]) { // do not forget " - n"!
77             mat[mat[i]] = i;
78             ans += val[mat[i]][i - n];
79         }
80     }
81     printf("%lld\n", ans);
82     for(int i = 1; i <= n; i++) {
83         printf("%d ", mat[i] ? mat[i] - n : mat[i]);
84     }
85     return 0;
86 }

AC代码

最后感谢YYB神犇,我是照抄他的代码的%%%%%%。

原文地址:https://www.cnblogs.com/huyufeifei/p/10350763.html

时间: 2024-08-02 03:55:05

uoj#80 二分图最大权匹配的相关文章

UOJ#80 二分图最大权匹配 [模板题]

从前一个和谐的班级,有 nlnl 个是男生,有 nrnr 个是女生.编号分别为 1,…,nl1,…,nl 和 1,…,nr1,…,nr. 有若干个这样的条件:第 vv 个男生和第 uu 个女生愿意结为配偶,且结为配偶后幸福程度为 ww. 请问这个班级里幸福程度之和最大是多少? 输入格式 第一行三个正整数,nl,nr,mnl,nr,m. 接下来 mm 行,每行三个整数 v,u,wv,u,w 表示第 vv 个男生和第 uu 个女生愿意结为配偶,且幸福程度为 ww.保证 1≤v≤nl1≤v≤nl,1≤

二分图最大权匹配(KM算法)

#80. 二分图最大权匹配 统计 描述 提交 自定义测试 从前一个和谐的班级,有 $n_l$ 个是男生,有 $n_r$ 个是女生.编号分别为 $1, \dots, n_l$ 和 $1, \dots, n_r$. 有若干个这样的条件:第 $v$ 个男生和第 $u$ 个女生愿意结为配偶,且结为配偶后幸福程度为 $w$. 请问这个班级里幸福程度之和最大是多少? 输入格式 第一行三个正整数,$n_l, n_r, m$. 接下来 $m$ 行,每行三个整数 $v, u, w$ 表示第 $v$ 个男生和第 $

[hdu1533]二分图最大权匹配 || 最小费用最大流

题意:给一个n*m的地图,'m'表示人,'H'表示房子,求所有人都回到房子所走的距离之和的最小值(距离为曼哈顿距离). 思路:比较明显的二分图最大权匹配模型,将每个人向房子连一条边,边权为曼哈顿距离的相反数(由于是求最小,所以先取反后求最大,最后再取反回来即可),然后用KM算法跑一遍然后取反就是答案.还可以用最小费用最大流做,方法是:从源点向每个人连一条边,容量为1,费用为0,从每个房子向汇点连一条边,容量为1,费用为0,从每个人向每个房子连一条边,容量为1,费用为曼哈顿距离的值,建好图后跑一遍

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

奔小康赚大钱 Problem Description 传说在遥远的地方有一个很富裕的村落,有一天,村长决定进行制度改革:又一次分配房子. 这但是一件大事,关系到人民的住房问题啊. 村里共同拥有n间房间,刚好有n家老百姓,考虑到每家都要有房住(假设有老百姓没房子住的话.easy引起不安定因素),每家必须分配到一间房子且仅仅能得到一间房子. 还有一方面,村长和另外的村领导希望得到最大的效益,这样村里的机构才会有钱.因为老百姓都比較富裕,他们都能对每一间房子在他们的经济范围内出一定的价格,比方有3间房

HDU2255 奔小康赚大钱 —— 二分图最大权匹配

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2255 奔小康赚大钱 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 11022    Accepted Submission(s): 4877 Problem Description 传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行

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

Going Home Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 22088   Accepted: 11155 Description On a grid map there are n little men and n houses. In each unit time, every little man can move one unit step, either horizontally, or vertica

ural 1076 Trash 二分图最大权匹配(费用流实现)

统计每种垃圾的总和,若将K种垃圾倒入第F个垃圾桶,那么花费就是K-F(k)  (自己已经有的垃圾不用倒). 然后就是简单的二分图建图. #include<cstdio> #include<queue> #include<algorithm> #include<cstring> using namespace std; #define MAXN 1000 #define MAXM 1000000 #define INF 0x3f3f3f3f struct no

hdu 2255 二分图最大权匹配 *

题意:说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子.这可是一件大事,关系到人民的住房问题啊.村里共有n间房间,刚好有n家老百姓,考虑到每家都要有房住(如果有老百姓没房子住的话,容易引起不安定因素),每家必须分配到一间房子且只能得到一间房子.另 一方面,村长和另外的村领导希望得到最大的效益,这样村里的机构才会有钱.由于老百姓都比较富裕,他们都能对每一间房子在他们的经济范围内出一定的价格, 比如有3间房子,一家老百姓可以对第一间出10万,对第2间出2万,对第3间出2

BZOJ 1937: [Shoi2004]Mst 最小生成树 [二分图最大权匹配]

传送门 题意: 给一张无向图和一棵生成树,改变一些边的权值使生成树为最小生成树,代价为改变权值和的绝对值,求最小代价 线性规划的形式: $Min\quad \sum\limits_{i=1}^{m} \delta_i$ $Sat\quad $非树边边权$\ge$生成树上路径任何一条边的边权 $i$非树边$j$树边 $w_i+\delta_i \ge w_j-\delta_j$ 然后可以转化成二分图最小顶标和来求解 这里需要求二分图最大权非完美匹配,我的做法是遇到$d[t] < 0$就退出,反正这