NEUOJ1212 Coach's plan (二分答案+二分圖匹配)

題目鏈接

2017ICPCECIC北方邀請賽I題

題目大意

有n個學生,m臺電腦,每個學生使用每臺電腦都對應一個適合值,現在讓你給每個學生分配一臺電腦(每臺電腦僅可以分配給一個學生),問這些學生中最小的適合值最大是多少。

分析

最小值最大化問題,很容易想到二分答案,於是最優化問題就轉換爲判定性問題:即能否給每個學生分配一臺電腦(每臺電腦僅可以分配給一個學生)且每對人機的適合值都大於等於mid。
這種分配問題,往往可以聯繫到二分圖。把n個人看成一類結點,把m臺電腦看成一類結點,則人機之間舒適度的關係就是二分圖。因此每次判定時將適合度權值大於等於mid的邊連上構成一個二分圖,若這個二分圖的最大匹配數等於n,則這樣的分配方案能實現。
PS:如果題目改成:“有一個n*m的矩陣,每個格子有一個數,現在要在矩陣中每一行取一個數,且同一列最多隻能取1個數求所取的n個數的最小值的最大值”這樣應該會更有思維含量,因爲把二維矩陣轉化爲二分圖是一種化歸,二分圖比較多的題目以這樣的形式出現,故作此總結。

代碼

#include <iostream>
#include <cstdio>
#include <cstring>
const int INF=1001;
const int MAXN=1005;
const int MAXM=2*500*500+10;
using namespace std;
struct Edge
{
    int to,next;
}edge[MAXM];
int w[MAXN][MAXN],head[MAXN],match[MAXN],vis[MAXN],edgenum,n,m;

void Add_edge(int u,int v)
{
    edge[++edgenum].to=v;
    edge[edgenum].next=head[u];
    head[u]=edgenum;
}

bool dfs(int u)
{
    for (int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if (!vis[v])
        {
            vis[v]=1;
            if (match[v]==-1||dfs(match[v]))
            {
                match[v]=u;
                match[u]=v;
                return true;
            }
        }
    }
    return false;
}

int hungarian()//匈牙利算法
{
    int cnt=0;
    memset(match,-1,sizeof(match));
    for (int i=1;i<=n+m;i++)
    {
        if (match[i]==-1)
        {
            memset(vis,0,sizeof(vis));
            if (dfs(i))
                cnt++;
        }
    }
    return cnt;
}
bool ok(int x)//轉化爲判定性問題
{
    edgenum=0;
    memset(head,-1,sizeof(head));
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            if (w[i][j]>=x)
            {
                Add_edge(i,j+n);//注意二分圖是無向圖
                Add_edge(j+n,i);
            }
    return (hungarian()==n);
}

int main()
{
    while (scanf("%d%d",&n,&m)!=EOF)
    {
        for (int i=1;i<=n;i++)
            for (int j=1;j<=m;j++)
                scanf("%d",&w[i][j]);
        /*bi_search_ans*/
        int low=0,high=INF;
        while (high-low>1)
        {
            int mid=(low+high)>>1;
            if (ok(mid))
                low=mid;
            else
                high=mid;
        }
        printf("%d\n",low);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章