題目鏈接
題目大意
有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;
}