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; }