HDU 3395 Special Fish(二分图中最优匹配)

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值