【學時總結】 ◆學時·III◆ 二分圖

来源:https://www.cnblogs.com/LuckyGlass-blog/archive/2018/05/19/9061140.html
-Advertisement-
Play Games

圖論是演算法競賽的一大板塊,二分圖又是其中一個重要的特殊模型——好像有點像網路流QwQ 例題:eXam(SGU 172)、The Perfect Stall(POJ 1274)、Machine Schedule(POJ 1325) ...


【學時·III】 二分圖


■基本策略■

其實本質是圖論中的網路流

二分圖是兩個由多個點組成的集合(上部和下部,且沒有重疊),兩個集合中的點不與該集合內其他的點連通,但和另一個集合內的點連通。我們稱這兩個集合為上部、下部,或X、Y部,比如:
這裡寫圖片描述

  • 判定

我們可以通過染色的方法將一個普通的連通圖轉換為二分圖(如果不是連通圖,則說明該圖存在多個二分圖或不為二分圖)。由於X部只與Y部相連,Y部也只與X部相連,我們可以把X、Y部染成不同的顏色。通過BFS(DFS也可以)從圖裡的一個點開始,假設它為X部,則與它相連的點為Y部,之後又為X部……直到訪問到一個標記過的點,且該點的標記與將要作的標記不同,則不是二分圖。將所有點標記完後還沒有衝突,則是二分圖。

  • 演算法

與二分圖相關的有匈牙利演算法、König定理,分別處理增廣路和最大匹配問題。

  • 最大匹配

二分圖中若存在邊集 E() 使得其中的邊沒有交點(共同的頂點),則稱 E() 是該二分圖的一個匹配。

特別的,若 E() 所含的頂點恰好是二分圖中所有的頂點,則稱 E() 為完全匹配。

最常考的是最大匹配,此時 E() 所包含的邊的數量達到二分圖中可能的最大數量。

  • 增廣路徑

邊集 E() 為二分圖已經匹配的邊,路徑P連接不同部的未匹配的點,若在P中匹配的邊和未匹配的邊交替出現,則稱P為增廣路徑。可見P的邊數一定是奇數,且因為起點和終點都未匹配,所以匹配邊比未匹配邊少1。

通過將增廣路徑反色——未匹配邊換為匹配邊,匹配邊換為未匹配邊,我們可以得到一個更好的匹配。當沒有增廣路徑時,形成的匹配就是該二分圖的最大匹配。

這種演算法稱為匈牙利演算法。


■來一點版題■

◆沒有技術含量◆ eXam

只要知道二分圖的定義就可以了

這道題只需要判斷給出的數據是否合法,且數據完美地分為X部(考試)、Y部(學生),因此就是一個標準的非連通圖二分圖判斷。

考試與學生的關係可以看做連線,這樣我們就得到了一個圖,其實是多個圖。可以通過遍歷每一個沒有標記的點來判別每一個連通圖是否都是二分圖,只要有一個不是,就判斷"no"。這裡作者用vector 的鄰接表儲存圖,col[] 儲存標記。
這裡的方案其實就是X、Y部中的某一部。(⊙_⊙)

  • 源代碼
/*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
int n_cla,n_stu,col[30005],error;
vector<int> vec[30005];
vector<int> ans;
void Solve(int u,int c)
{
    col[u]=c;
    if(c%2) ans.push_back(u);
    for(int i=0;i<(int)vec[u].size();i++)
    {
        int v=vec[u][i];
        if(col[v]){if((c+1)%2!=col[v]%2)error=true;continue;}
        Solve(v,c+1);
        if(error) return;
    }
}
int main()
{
    scanf("%d%d",&n_cla,&n_stu);
    for(int i=1;i<=n_stu;i++)
    {
        int A,B;scanf("%d%d",&A,&B);
        vec[B].push_back(A);vec[A].push_back(B);
    }
    for(int i=1;i<=n_cla;i++)
        if(!col[i])
        {
            Solve(i,1);
            if(error)
            {
                puts("no");
                return 0;
            }
        }
    printf("yes\n%d\n%d",ans.size(),ans[0]);
    for(int i=1;i<(int)ans.size();i++)
        printf(" %d",ans[i]);
    return 0;
}

◆最大匹配◆ The Perfect Stall

把匈牙利演算法的板套上去

  1. 圖的建立

雖然二分圖分為2個部(牛欄、奶牛),但實際上它還是一個圖——所以必須將2個部的點放入同一個圖中。這裡可以通過將牛放入1~N的點,把牛欄放入N+1~N+M的點中。

  1. 匈牙利演算法的實現

尋找增廣路徑一般是採用DFS:

bool DFS(int u)
{
    for(int i=0;i<(int)vec[u].size();i++) //枚舉鄰接點
    {
        int v=vec[u][i];
        if(vis[v]) continue; //之前沒有訪問
        vis[v]=true; //標記
        if(mat[v]==0 || DFS(mat[v])) //如果該點沒有匹配,或通過該點能向下找到一個未匹配點
        { //這就是一條增廣路徑
            mat[u]=v;mat[v]=u; //反邊
            return true;
        }
    }
    return false;
}

這道題比較基礎,只要求找到最大匹配的數量,因此直接使用匈牙利演算法,統計有多少組匹配就可以了。

  • 源代碼
/*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
#define MAXN 200
int n,m,mat[2*MAXN+5];
bool vis[2*MAXN+5];
vector<int> vec[2*MAXN+5];
bool DFS(int u)
{
    for(int i=0;i<(int)vec[u].size();i++)
    {
        int v=vec[u][i];
        if(vis[v]) continue;
        vis[v]=true;
        if(mat[v]==0 || DFS(mat[v]))
        {
            mat[u]=v;mat[v]=u;
            return true;
        }
    }
    return false;
}
int Solve()
{
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        memset(vis,false,sizeof vis);
        if(mat[i]==0 && DFS(i))
            ans++;
    }
    return ans;
}
int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        memset(mat,0,sizeof mat);
        for(int i=1;i<=n;i++)
        {
            int n_;scanf("%d",&n_);
            for(int j=0,x;j<n_;j++)
            {
                scanf("%d",&x);
                vec[i].push_back(x+n);
                vec[x+n].push_back(i);
            }
        }
        printf("%d\n",Solve());
        for(int i=1;i<=n;i++) vec[i].erase(vec[i].begin(),vec[i].end());
        for(int i=1;i<=m;i++) vec[i+n].erase(vec[i+n].begin(),vec[i+n].end());
    }
    return 0;
}

◆最小覆蓋◆ Machine Schedule

就像一道結論題,結論一發現,就沒什麼難點了

  1. König定理

最小覆蓋點數=最大匹配數

下麵是證明:
這裡寫圖片描述
這裡寫圖片描述

  1. 所以這道題還是最大匹配~
  • 源代碼
/*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
#define MAXN 200
int n,m,mat[2*MAXN+5];
bool vis[2*MAXN+5];
vector<int> vec[2*MAXN+5];
bool DFS(int u)
{
    for(int i=0;i<(int)vec[u].size();i++)
    {
        int v=vec[u][i];
        if(vis[v]) continue;
        vis[v]=true;
        if(mat[v]==0 || DFS(mat[v]))
        {
            mat[u]=v;mat[v]=u;
            return true;
        }
    }
    return false;
}
int Solve()
{
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        memset(vis,false,sizeof vis);
        if(mat[i]==0 && DFS(i))
            ans++;
    }
    return ans;
}
int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        memset(mat,0,sizeof mat);
        for(int i=1;i<=n;i++)
        {
            int n_;scanf("%d",&n_);
            for(int j=0,x;j<n_;j++)
            {
                scanf("%d",&x);
                vec[i].push_back(x+n);
                vec[x+n].push_back(i);
            }
        }
        printf("%d\n",Solve());
        for(int i=1;i<=n;i++) vec[i].erase(vec[i].begin(),vec[i].end());
        for(int i=1;i<=m;i++) vec[i+n].erase(vec[i+n].begin(),vec[i+n].end());
    }
    return 0;
}

The End

Thanks for reading!

- Lucky_Glass


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 醒來的時候登QQ發現有人找我要一份貼吧爬蟲的源代碼,想起之前練手的時候寫過一個抓取百度貼吧發帖記錄中的郵箱與手機號的爬蟲,於是開源分享給大家學習與參考。 需求分析: 本爬蟲主要是對百度貼吧中各種帖子的內容進行抓取,並且分析帖子內容將其中的手機號和郵箱地址抓取出來。主要流程在代碼註釋中有詳細解釋。 測 ...
  • 第一部分:英文單詞記錄 1、drug 藥物 2、repositioning 重新佈置,重新定位 3、comprehensive 綜合 4、Bi-random walk algorithm 隨機游走演算法 5、motivation 動機 6、aims to 以...為目標 7、identity 確定 8 ...
  • 環境:Python3.6+Windows 開發工具:你喜歡用啥就用啥,總而言之,言而總之 你開心就好 使用的Python模塊 requests Requests 是用Python語言編寫,基於urllib,採用 Apache2 Licensed 開源協議的 HTTP 庫。它比urllib 更加方便, ...
  • 聲明:本人的一切著作,禁止用於以營銷為目的的任何轉載! 前言 很久以前就聽說 Python 的 async/await 很厲害,但是直到現在都沒有用過,一直都在用多線程模型來解決各種問題。最近看到隔壁的 Go 又很火,所以決定花時間研究下 Python 協程相關的內容,終於在翻閱了一褲衩的資料之後有 ...
  • Matlab學習筆記一、 Desktop Basics (Matlab 基礎知識)當你打開Matlab的時候,matlab按照以下預設的方式展示出來。該桌面主要包括以下幾部分內容:當前文件夾:當前文件夾下麵的文件命令視窗:在該視窗輸入命令,提示符為>>工作區:顯示所有用戶輸入和創建的變數名字以及內容... ...
  • HTTPS(HTTP over SSL)是以安全為目標的 HTTP 通道,可以理解為 HTTP + SSL/TLS,即在 HTTP 下加入 SSL/TLS 層作為安全基礎。其中 TLS 的前身是 SSL,目前廣泛使用的是 TLS 1.2。本文主要針對 TLS 在 HTTPS 中對性能影響的對應調優方... ...
  • Description 菲菲和牛牛在一塊n行m列的棋盤上下棋,菲菲執黑棋先手,牛牛執白棋後手。棋局開始時,棋盤上沒有任何棋子, 兩人輪流在格子上落子,直到填滿棋盤時結束。落子的規則是:一個格子可以落子當且僅當這個格子內沒有棋子且 這個格子的左側及上方的所有格子內都有棋子。 棋盤的每個格子上,都寫有兩 ...
  • Java集合類的詳解與應用 集合簡介: 1.定義:可以同時存儲不同類型的數據 他的存儲空間會隨著數據的增大而增大 2.缺點:只能存儲引用數據類型 3.優點:更加合理的利用空間,封裝了更多的方法,用起來更加方便 4.分類:集合分為:Collection(介面): List介面:ArrayList類,L ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...