题目链接
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;
}