1 #include <cstdio>
2 #include <cstring>
3 #include <queue>
4 #include <vector>
5 #include <algorithm>
6 using namespace std;
7 /******************************************************
8 * 二分图匹配(匈牙利算法得邻接矩阵+dfs实现)
9 * 初始化:g[][]两边顶点的划分情况
10 * 建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配
11 * g 没有边相连则初始化为0
12 * //un是匹配左边的顶点数,vn是匹配右边的顶点数
13 * 调用:res = hungray():输出最大匹配
14 * 优点:适用于稠密图,dfs找增广路,实现简洁易于理解
15 * 时间复杂度:O(V,E)。
16 * 顶点编号从0开始
17 const int maxn = 500 + 5;
18 int un, vn;//二分图中u, v的数目,使用前必须赋值
19 int g[maxn][maxn];
20 int linker[maxn];//每个结点的匹配结点
21 bool used[maxn];
22
23 bool dfs(int u) {
24 for(int v = 0; v < vn; v ++) {
25 if(g[u][v] && !used[v]) {
26 used[v] = true;
27 if(linker[v] == -1 || dfs(linker[v])) {
28 linker[v] = u;
29 return true;
30 }
31 }
32 }
33 return false;
34 }
35
36 int hungary() {
37 int res = 0;
38 memset(linker, -1, sizeof linker);
39 for(int u = 0; u < un; u ++) {
40 memset(used, false, sizeof used);
41 if(dfs(u)) res ++;
42 }
43 return res;
44 }
45 * ***************************************************/
46
47 /******************************************************
48 * 匈牙利算法链式前向星实现
49 * 使用前init()进行初始化,给un赋值
50 * 加边使用函数addedge(u, v)
51
52 const int maxn = 5000 + 5;//点数的最大值
53 const int maxm = 50000 + 5;//边数的最大值
54 struct Edge {
55 int to, next;
56 } edge[maxm];
57 int head[maxn], tot;
58 int linker[maxn];//每个结点的匹配结点
59 bool used[maxn];
60 int un;//匹配u结点的个数
61
62 void init() {
63 tot = 0;
64 memset(head, -1, sizeof head);
65 }
66
67 void addedge(int u, int v) {//添加边的时候记得要让左侧匹配结点u的编号为[0, un)
68 edge[tot].to = v; edge[tot].next = head[u];
69 head[u] = tot ++;
70 }
71
72 bool dfs(int u) {
73 for(int i = head[u]; ~i; i = edge[i].next) {
74 int v = edge[i].to;
75 if(!used[v]) {
76 used[v] = true;
77 if(linker[v] == -1 || dfs(linker[v])) {
78 linker[v] = u;
79 return true;
80 }
81 }
82 }
83 return false;
84 }
85
86 int hungary() {
87 int res = 0;
88 memset(linker, -1, sizeof linker);
89 for(int u = 0; u < un; u ++) {
90 memset(used, false, sizeof used);
91 if(dfs(u)) res ++;
92 }
93 return res;
94 }
95 * ****************************************************/
96
97 /*******************************************************
98 * 二分图匹配(Hopcroft-Karp算法)
99 * 复杂度:O(sqrt(n) * E)
100 * 邻接表存图, vector实现
101 * vector先初始化,然后加边
102 * un为左端的顶点数,使用前赋值(点编号0开始)
103
104 const int maxn = 3000 + 5;
105 const int inf = 0x3f3f3f3f;
106 vector <int> G[maxn];
107 int un;
108 int mx[maxn], my[maxn];
109 int dx[maxn], dy[maxn];
110 int dis;
111 bool used[maxn];
112 bool searchp() {
113 queue <int> que;
114 dis = inf;
115 memset(dx, -1, sizeof dx);
116 memset(dy, -1, sizeof dy);
117 for(int i = 0; i < un; i ++) {
118 if(mx[i] == -1) {
119 que.push(i);
120 dx[i] = 0;
121 }
122 }
123 while(!que.empty()) {
124 int u = que.front();
125 que.pop();
126 if(dx[u] >dis) break;
127 int sz = G[u].size();
128 for(int i = 0; i < sz; i ++) {
129 int v = G[u][i];
130 if(dy[v] == -1) {
131 dy[v] = dx[u] + 1;
132 if(my[v] == -1) dis = dy[v];
133 else {
134 dx[my[v]] = dy[v] + 1;
135 que.push(my[v]);
136 }
137 }
138 }
139 }
140 return dis != inf;
141 }
142
143 bool dfs(int u) {
144 int sz = G[u].size();
145 for(int i = 0; i < sz; i ++) {
146 int v = G[u][i];
147 if(!used[v] && dy[v] == dx[u] + 1) {
148 used[v] = true;
149 if(my[v] != -1 && dy[v] == dis) continue;
150 if(my[v] == -1 || dfs(my[v])) {
151 my[v] = u;
152 mx[u] = v;
153 return true;
154 }
155 }
156 }
157 return false;
158 }
159
160 int maxmatch() {
161 int res = 0;
162 memset(mx, -1, sizeof mx);
163 memset(my, -1, sizeof my);
164 while(searchp()) {
165 memset(used, false, sizeof used);
166 for(int i = 0; i < un; i ++) {
167 if(mx[i] == -1 && dfs(i))
168 res ++;
169 }
170 }
171 return res;
172 }
173 * ****************************************************/
174
175 /*******************************************************
176 * 二分多重匹配
177
178 const int maxn = 1000 + 5;
179 const int maxm = 500 + 5;
180 int un, vn;
181 int g[maxn][maxn];
182 int linker[maxm][maxn];
183 bool used[maxm];
184 int num[maxm];//右边最大的匹配数
185
186 bool dfs(int u) {
187 for(int v = 0; v < vn; v ++) {
188 if(g[u][v] && !used[v]) {
189 used[v] = true;
190 if(linker[v][0] < num[v]) {
191 linker[v][++ linker[v][0]] = u;
192 return true;
193 }
194 for(int i = 1; i <= num[v]; i ++) {
195 if(dfs(linker[v][i])) {
196 linker[v][i] = u;
197 return true;
198 }
199 }
200 }
201 }
202 return false;
203 }
204
205 int hungary() {
206 int res = 0;
207 for(int i = 0; i < vn; i ++)
208 linker[i][0] = 0;
209 for(int u = 0; u < un; u ++) {
210 memset(used, false, sizeof used);
211 if(dfs(u)) res ++;
212 }
213 return res;
214 }
215 * *****************************************************/
216
217 /********************************************************
218 * KM算法
219 * 复杂度 O(nx * nx * ny)
220 * 求最大权匹配
221 * 若最小权匹配,可将权值取相反数,结果取相反数
222 * 点的编号从0开始
223
224 const int maxn = 300 + 5;
225 const int inf = 0x3f3f3f3f;
226 int nx, ny;
227 int g[maxn][maxn];
228 int linker[maxn], lx[maxn], ly[maxn];// y中个点匹配状态,x, y中的点标号
229 int slack[maxn];
230 bool visx[maxn], visy[maxn];
231
232 bool dfs(int x) {
233 visx[x] = true;
234 for(int y = 0; y < ny; y ++) {
235 if(visy[y]) continue;
236 int tmp = lx[x] + ly[y] - g[x][y];
237 if(tmp == 0) {
238 visy[y] = true;
239 if(linker[y] == -1 || dfs(linker[y])) {
240 linker[y] = x;
241 return true;
242 }
243 }
244 else if(slack[y] > tmp)
245 slack[y] = tmp;
246 }
247 return false;
248 }
249
250 int km() {
251 memset(linker, -1, sizeof linker);
252 memset(ly, 0, sizeof ly);
253 for(int i = 0; i < nx; i ++) {
254 lx[i] = -inf;
255 for(int j = 0; j < ny; j ++)
256 if(g[i][j] > lx[i])
257 lx[i] = g[i][j];
258 }
259 for(int x = 0; x < nx; x ++) {
260 for(int i = 0; i < ny; i ++)
261 slack[i] = inf;
262 while(true) {
263 memset(visx, false, sizeof visx);
264 memset(visy, false, sizeof visy);
265 if(dfs(x)) break;
266 int d = inf;
267 for(int i = 0; i < ny; i ++)
268 if(!visy[i] && d > slack[i])
269 d = slack[i];
270 for(int i = 0; i < nx; i ++)
271 if(visx[i])
272 lx[i] -= d;
273 for(int i = 0; i < ny; i ++) {
274 if(visy[i]) ly[i] += d;
275 else slack[i] -= d;
276 }
277 }
278 }
279 int res = 0;
280 for(int i = 0; i < ny; i ++)
281 if(linker[i] != -1)
282 res += g[linker[i]][i];
283 return res;
284 }
285 * *****************************************************/
286
287 /*******************************************************
288 * 最小支配集
289 const int maxn = 1000 + 5;
290 int pre[maxn];//存储父节点
291 bool visit[maxn];//DFS标记数组
292 int newpos[maxn];//遍历序列
293 int now;
294 int n, m;
295
296 int head[maxn];//链式前向星
297 struct Node {int to; int next;};
298 Node edge[maxn];
299
300 void DFS(int x) {
301 newpos[now ++] = x;//记录遍历序列
302 for(int k = head[x]; k != -1; k = edge[k].next) {
303 if(!visit[ edge[k].to ]) {
304 visit[ edge[k].to ] = true;
305 pre[edge[k].to] = x;//记录父节点
306 DFS(edge[k].to);
307 }
308 }
309 }
310
311 int MDS() {
312 bool s[maxn] = {0};
313 bool set[maxn] = {0};
314 int ans = 0;
315 for(int i = n - 1; i >= 0; i--) {//逆序进行贪心
316 int t = newpos[i];
317 if(!s[t]) { //如果当前点没被覆盖
318 if(! set[ pre[t] ]) {//当前点的父节点不属于支配集
319 set[ pre[t] ] = true;//当前点的父节点加入支配集
320 ans ++; //支配集节点个数加 1
321 }
322 s[t] = true; //标记当前点已被覆盖
323 s[ pre[t] ] = true;// 标记当前点的父节点被覆盖
324 s[ pre[ pre[t] ] ] = true;//标记当前点的父节点的父节点被覆盖
325 }
326 }
327 return ans;
328 }
329
330 void solve() {
331 memset(visit, false, sizeof(visit));//初始化
332 now = 0;
333 visit[1] = true;
334 pre[1] = 1;
335 DFS(1);//从根节点开始寻摘遍历序列
336 MDS();
337 }
338 * ****************************************************/
339
340 /*******************************************************
341 * 最小点覆盖
342 const int maxn = 1000 + 5;
343 int pre[maxn];//存储父节点
344 bool visit[maxn];//DFS标记数组
345 int newpos[maxn];//遍历序列
346 int now;
347 int n, m;
348
349 int head[maxn];//链式前向星
350 struct Node {int to; int next;};
351 Node edge[maxn];
352
353 void DFS(int x) {
354 newpos[now ++] = x;//记录遍历序列
355 for(int k = head[x]; k != -1; k = edge[k].next) {
356 if(!visit[ edge[k].to ]) {
357 visit[ edge[k].to ] = true;
358 pre[edge[k].to] = x;//记录父节点
359 DFS(edge[k].to);
360 }
361 }
362 }
363
364 int MVC() {
365 bool s[maxn] = {0};
366 bool set[maxn] = {0};
367 int ans = 0;
368 for(int i = n - 1; i >= 1; i--) {//逆序进行贪心,排除掉其根节点
369 int t = newpos[i];
370 if(!s[t] && !s[ pre[t] ]) {//如果当前节点和其父节点都不属于顶点覆盖集合
371 set[ pre[t] ] = true;//把其父节点加入到顶点覆盖集合
372 ans ++; //集合内顶点个数加 1
373 s[t] = true;//标记当前节点被覆盖
374 s[ pre[t] ] = true;//标记其父节点被覆盖
375 }
376 }
377 return ans;
378 }
379
380 void solve() {
381 memset(visit, false, sizeof(visit));//初始化
382 now = 0;
383 visit[1] = true;
384 pre[1] = 1;
385 DFS(1);//从第一个根节点开始寻找遍历序列
386 MVC();
387 }
388 * * ****************************************************/
389
390 /*******************************************************
391 * 最大独立集
392
393 const int maxn = 1000 + 5;
394 int pre[maxn];//存储父节点
395 bool visit[maxn];//DFS标记数组
396 int newpos[maxn];//遍历序列
397 int now;
398 int n, m;
399
400 int head[maxn];//链式前向星
401 struct Node {int to; int next;};
402 Node edge[maxn];
403
404 void DFS(int x) {
405 newpos[now ++] = x;//记录遍历序列
406 for(int k = head[x]; k != -1; k = edge[k].next) {
407 if(!visit[ edge[k].to ]) {
408 visit[ edge[k].to ] = true;
409 pre[edge[k].to] = x;//记录父节点
410 DFS(edge[k].to);
411 }
412 }
413 }
414
415 int MIS() {
416 bool s[maxn] = {0};
417 bool set[maxn] = {0};
418 int ans = 0;
419 for(int i = n - 1; i >= 0; i--) {//按照DFS遍历序列的逆序进行贪心
420 int t = newpos[i];
421 if(!s[t]) {//如果当前节点没有被覆盖
422 set[t] = true;//把当前节点加入到独立集
423 ans ++;//独立集中点的个数加 1
424 s[t] = true;//标记当前点已经被覆盖
425 s[ pre[t] ] = true;//标记当前点的父节点已经被覆盖
426 }
427 }
428 return ans;
429 }
430
431 void solve() {
432 memset(visit, false, sizeof(visit));//初始化
433 now = 0;
434 visit[1] = true;
435 pre[1] = 1;
436 DFS(1);//从第一个根节点开始寻找遍历序列
437 MIS();
438 }
439 * * ***************************************************/
440
441
442 int main() {
443
444 return 0;
445 }
来源:https://www.cnblogs.com/bianjunting/p/11376882.html