HDU 3395 Special Fish(二分图中最优匹配)
http://acm.hdu.edu.cn/showproblem.php?pid=3395
题意:
武大荷塘有N条鱼(不分性别),每条鱼有一个价值vi.且给出一个N*N的矩阵,矩阵中(i,j)格为1表示,第i条鱼会攻击第j条鱼并产下卵.
产卵的数量= vi XOR vj. 现在每条鱼只能被攻击1次(一条鱼只能攻击1次且被攻击1次),且每条鱼只会攻击它可能会攻击的所有鱼中的一条(哪些其他鱼它会攻击已经在N*N矩阵中给出).现在要你求这N条鱼产卵数目的最大值.
分析:
由于每次产卵活动都是由一条鱼攻击另一条鱼构成的.且任意两次攻击活动的 攻击鱼必然不同,被攻击鱼也必然不同(一条鱼只能攻击1次且被攻击1次). 所以其他这就是一条带权值的匹配边了.
建立二分图: 将N条鱼分成左右两个点集,左点集放1到N条攻击的鱼,右点集放1到N条被攻击的鱼.如果左i会攻击右j,那么就在左i与右j之间连一条权值为它们产卵数目的边.(如果不会攻击,那就连一条权值为0的边)
所以我们的目的就是要找出一个权值和(攻击产卵数)最大的匹配了。
结论:一个产卵数目最多的方案必与一个二分图的最优匹配一一对应.(假设有100条鱼且产卵数目最多的方案是1攻击2,2攻击3,3攻击4,其他鱼不攻击.那么匹配边就是1->2’ 2->3’ 3->4’ 其他边随意取权值0的边,因为是完全图.反之二分图的最优匹配必然也能对应出一个产卵数目的方案. )
所以如果有更优的产卵数目方案,那么它必定能对应出一个更优的二分图最优匹配,所以我们如果用KM算法不会丢失最优产卵方案.那么我们会不会用过KM算法求得一个不存在对应产卵方案的最优匹配?不会,因为两者必然是一一对应的映射关系.
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100+5;
struct Max_Match
{
int n,W[maxn][maxn];
int Lx[maxn],Ly[maxn];
bool S[maxn],T[maxn];
int left[maxn];
bool match(int i)
{
S[i]=true;
for(int j=1;j<=n;j++)if(Lx[i]+Ly[j]==W[i][j] && !T[j])
{
T[j]=true;
if(left[j]==-1 || match(left[j]))
{
left[j]=i;
return true;
}
}
return false;
}
void update()
{
int a=1<<30;
for(int i=1;i<=n;i++)if(S[i])
for(int j=1;j<=n;j++)if(!T[j])
a=min(a, Lx[i]+Ly[j]-W[i][j]);
for(int i=1;i<=n;i++)
{
if(S[i]) Lx[i] -=a;
if(T[i]) Ly[i] +=a;
}
}
int solve(int n)
{
this->n=n;
memset(left,-1,sizeof(left));
for(int i=1;i<=n;i++)
{
Lx[i]=Ly[i]=0;
for(int j=1;j<=n;j++) Lx[i]=max(Lx[i],W[i][j]);
}
for(int i=1;i<=n;i++)
{
while(true)
{
for(int j=1;j<=n;j++) S[j]=T[j]=false;
if(match(i)) break;
else update();
}
}
int ans=0;
for(int i=1;i<=n;i++)
ans+= W[left[i]][i];
return ans;
}
}KM;
int main()
{
int n,val[maxn];
while(scanf("%d",&n)==1 && n)
{
for(int i=1;i<=n;i++) scanf("%d",&val[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
char v;
scanf(" %c",&v);
if(v=='1') KM.W[i][j]= val[i]^val[j];
else KM.W[i][j]=0;
}
printf("%d\n",KM.solve(n));
}
return 0;
}