• 二分匹配模板


      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 }
  • 相关阅读:
    Gitee + PicGo搭建图床 & Typora上传图片到图床
    算法思维 ---- 双指针法
    Floyd's cycle-finding algorithm
    Boyer-Moore Voting Algorithm
    youtube-dl 使用小记
    算法思维 ---- 滑动窗口
    Memo
    英语
    BZOJ 3270
    BZOJ 3196
  • 原文地址:https://www.cnblogs.com/bianjunting/p/11376882.html
Copyright © 2020-2023  润新知