2016multi-university1005 KM算法

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=5727

题目大意

有一串项链,形成的是环,每个宝石都有自己的属性,例如阴阳。但是宝石连接的时候只能阴阳交错。但是在连接的时候,有的阳性的宝石和阴性的宝石,会让阳性的宝石失去价值,最后求最少有多少宝石在排列的时候,失去价值。

解析

因为在放置的时候,阴性影响阳性,所以先放置阴性,第一个不需要变化,全排列后面的阴性宝石就好,在放入阳性的宝石,放入后,如果被影响了,就加一条边带有权值-1的边。每一种排列后,建一次边,问题就转换为求二分图的最小权匹配。用KM算法便可以,但是KM需要优化,用slack数组。

代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 20;
const int INF = 0x3f3f3f3f;
int A[maxn][maxn];
int g[maxn][maxn];
int n, m;
int y[maxn];
int lx[maxn], ly[maxn];
int sx[maxn], sy[maxn];
int match[maxn];
int slack[maxn]; 
bool path(int u) {
    sx[u] = 1;
    for (int i=0; i<n; i++) {
        if (sy[i])
            continue;
        int tmp = lx[u]+ly[i]-g[u][i];
        if (tmp == 0)
        {
            sy[i] = 1;
            if (match[i] == -1 || path(match[i])) {
                match[i] = u;
                return true;
            }
        }
        else
            slack[i] = tmp;
    }
    return false;
}

int KM() {
    for (int i=0; i<n; i++) // 初始化标号
    {
        lx[i] = -INF;
        ly[i] = 0;
        for (int j=0; j<n; j++) 
            if (lx[i] < g[i][j])
                lx[i] = g[i][j];
    }
    memset(match, -1, sizeof(match));
    for (int k=0; k<n; k++) {
        for (int i=0; i<n; i++)
            slack[i] = INF;
        while (1)
        {
            memset(sx, 0, sizeof(sx));
            memset(sy, 0, sizeof(sy));
            if (path(k))  //没有匹配成功
                break;
            // 修改标号
            int dx = INF;
            for (int i=0; i<n; i++) 
                if (!sy[i] && dx > slack[i])
                    dx = slack[i];
            for (int i=0; i<n; i++)
            {
                if (sx[i])
                    lx[i] -= dx;
                if (sy[i])
                    ly[i] += dx;
                else
                    slack[i] -= dx; // slack[i]为了平衡,如果sy[i]没有被匹配过。 
            }
        }
    }
    int sum = 0;
    for (int i=0; i<n; i++)
        sum += g[match[i]][i];
    return sum;
}

int main() {
    while (scanf("%d%d", &n, &m)!=EOF) {
    if (n == 0) {
        puts("0");
            continue;
    }
        memset(A, 0, sizeof(A));
        for (int i=0; i<m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            A[u-1][v-1] = -1;
        }
        for (int i=0; i<n; i++) 
            y[i] = i;
        int ans = INF;
        do {
            memset(g, 0, sizeof(g));
            for (int i=0; i<n; i++) {
                for (int j=0; j<n; j++)
                    g[i][y[j]] = A[i][y[j]] | A[i][j?y[j-1]:y[n-1]]; //如果阳和阴放在一起时,阳减弱。便建边
            }
            ans = min(ans, -KM());//求最小权匹配
        }while (next_permutation(y+1, y+n));
        printf("%d\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值