二分图带权匹配

#include <cstdio>

#include <memory.h>

#include <algorithm>    // 使用其中的 min 函数

using namespace std;

const int MAX = 405;

int n;                // X 的大小

int weight [MAX] [MAX];        // X 到 Y 的映射(权重)

int lx [MAX], ly [MAX],a[MAX],ans[MAX];        // 标号

bool sx [MAX], sy [MAX];    // 是否被搜索过

int match [MAX];        // Y(i) 与 X(match [i]) 匹配

// 初始化权重

void init (int size);

// 从 X(u) 寻找增广道路,找到则返回 true

bool path (int u);

// 参数 maxsum 为 true ,返回最大权匹配,否则最小权匹配

int bestmatch (bool maxsum = true);

void init (int size)

{

    // 根据实际情况,添加代码以初始化

    n = size;

    int i,j,k,t;

    memset(weight,0,sizeof(weight));

    for (i=1;i<=n;i++) scanf("%d",&a[i-1]);

    for (i=0;i<n;i++)

    {

        scanf("%d",&k);

        for (j=0;j<k;j++)

        {

            scanf("%d",&t);

            weight[i][t-1]=a[i]*a[i];

        }

    }

}



bool path (int u)

{

    sx [u] = true;

    for (int v = 0; v < n; v ++)

        if (!sy [v] && lx[u] + ly [v] == weight [u] [v])

            {

            sy [v] = true;

            if (match [v] == -1 || path (match [v]))

                {

                match [v] = u;

                return true;

                }

            }

    return false;

}

int bestmatch (bool maxsum)

{

    int i, j;

    if (!maxsum)

        {

        for (i = 0; i < n; i ++)

            for (j = 0; j < n; j ++)

                weight [i] [j] = -weight [i] [j];

        }

    // 初始化标号

    for (i = 0; i < n; i ++)

        {

        lx [i] = -0x1FFFFFFF;

        ly [i] = 0;

        for (j = 0; j < n; j ++)

            if (lx [i] < weight [i] [j])

                lx [i] = weight [i] [j];

        }

    memset (match, -1, sizeof (match));

    for (int u = 0; u < n; u ++)

        while (1)

            {

            memset (sx, 0, sizeof (sx));

            memset (sy, 0, sizeof (sy));

            if (path (u))

                break;

            // 修改标号

            int dx = 0x7FFFFFFF;

            for (i = 0; i < n; i ++)

                if (sx [i])

                    for (j = 0; j < n; j ++)

                        if(!sy [j])

                            dx = min (lx[i] + ly [j] - weight [i] [j], dx);

            for (i = 0; i < n; i ++)

                {

                if (sx [i])

                    lx [i] -= dx;

                if (sy [i])

                    ly [i] += dx;

                }

            }

    int sum = 0;

    for (i = 0; i < n; i ++)

        sum += weight [match [i]] [i];

    if (!maxsum)

        {

        sum = -sum;

        for (i = 0; i < n; i ++)

            for (j = 0; j < n; j ++)

                weight [i] [j] = -weight [i] [j];         // 如果需要保持 weight [ ] [ ] 原来的值,这里需要将其还原

        }

    return sum;

}



int main()

{

    int n,i;

    scanf ("%d", &n);

    init (n);

    int cost = bestmatch (true);

    //printf ("%d\n", cost);

    //for (i=0;i<n;i++) printf("%d ",match[i]+1);

    for (i=0;i<n;i++)

    if (weight[match[i]][i]==0) ans[match[i]]=0;else

        ans[match[i]]=i+1;

    for (i=0;i<n-1;i++) printf("%d ",ans[i]);

    printf("%d",ans[n-1]);



    return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值