题目链接
题意
给定
n
×
n
n×n
n×n 个整数
a
i
j
(
1
≤
i
,
j
≤
n
)
a_{ij}(1≤i,j≤n)
aij(1≤i,j≤n),要找出
2
n
2n
2n 个整数
x
1
,
x
2
,
…
,
x
n
,
y
1
,
y
2
,
…
,
y
n
x1,x2,…,xn,y1,y2,…,yn
x1,x2,…,xn,y1,y2,…,yn 在满足
x
i
+
y
j
≤
a
i
,
j
(
1
≤
i
,
j
≤
n
)
xi+yj≤a_{i,j}(1≤i,j≤n)
xi+yj≤ai,j(1≤i,j≤n) 的约束下,最大化目标函数
∑
i
=
1
n
(
x
i
+
y
i
)
\sum_{i=1}^n(xi+yi)
∑i=1n(xi+yi)
求目标函数的最大值。
思路
找这题主要测试KM复杂度,dfs版找slack是伪
N
3
N^3
N3,这里记个bfs板子,KM学习点此处
KM算法是通过给每个顶点一个标号(叫做顶标)来把求最大权匹配的问题转化为求完备匹配的问题的,顶标满足
x
i
+
y
j
>
=
w
[
i
]
[
j
]
x_i+y_j>=w[i][j]
xi+yj>=w[i][j],求出其中最大的顶标和(即最大权匹配)
这题所约束的条件与KM中顶标性质相同,不过符号反了,那么交换等式左右两边即可。即反转w,反转顶标和。
代码
// 注意:如果只是想求最大权值匹配而不要求是完全匹配的话,请把各个不相连的边的权值设置为0。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N = 205;
const ll INF = 0x3f3f3f3f3f3f3f3f;
struct KM {
ll n;
ll e[N][N];
ll wx[N], wy[N], slack[N];
ll nxtx[N], nxty[N], visx[N], visy[N], pre[N], q[N], ql, qr;
ll check(ll now) {
visy[now] = 1;
if(nxty[now] != -1) {
if(!visx[nxty[now]]) q[++qr] = nxty[now], visx[nxty[now]] = 1;
return 0;
}
while(now != -1) swap(now, nxtx[nxty[now] = pre[now]]);
return 1; // 找到增广路
}
void bfs(ll s) {
memset(slack,0x3f,sizeof(slack));
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
q[ql = qr = 1] = s;
visx[s] = qr = 1;
ll d;
while(1) {
while(ql <= qr) {
ll u = q[ql++];
for(ll i = 1; i <= n; ++i) {
if(visy[i]) continue;
ll value = wx[u]+wy[i]-e[u][i];
if(slack[i] >= value) {
slack[i] = value;
pre[i] = u;
if(slack[i] == 0 && check(i)) return;
}
}
}
ll d = INF;
for(ll i = 1; i <= n; ++i) if(!visy[i] && slack[i]) d = min(d,slack[i]);
for(ll i = 1; i <= n; ++i) {
if(visx[i]) wx[i] -= d;
if(visy[i]) wy[i] += d;
else slack[i] -= d;
}
ql = 1, qr = 0;
for(ll i = 1; i <= n; ++i) if(!visy[i] && !slack[i] && check(i)) return;
}
}
ll getans() {
memset(nxtx,-1,sizeof(nxtx));
memset(nxty,-1,sizeof(nxty));
memset(wy,0,sizeof(wy));
for(ll i = 1; i <= n; ++i) wx[i] = *max_element(e[i]+1,e[i]+1+n);
for(ll i = 1; i <= n; ++i) bfs(i);
ll ans = 0;
for(ll i = 1; i <= n; ++i) if(nxtx[i] != -1) ans += e[i][nxtx[i]];
// 本题所求是顶标和,顶标和等于最大匹配
// for(ll i = 1; i <= n; ++i) ans += wx[i]+wy[i];
return ans;
}
}ac;
int main() {
ll t, ca = 1;
for(scanf("%lld",&t); ca <= t; ++ca) {
scanf("%lld",&ac.n);
for(ll i = 1; i <= ac.n; ++i) {
for(ll j = 1; j <= ac.n; ++j) {
scanf("%lld",&ac.e[i][j]);
ac.e[i][j] = -ac.e[i][j];
}
}
printf("Case #%lld: %lld\n",ca,-ac.getans());
}
return 0;
}