题目链接:HDU 2255 奔小康赚大钱
裸KM。
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAX_N = 300 + 30;
const int INF = 0x3f3f3f3f;
int n_x, n_y;//两边的点数
int G[MAX_N][MAX_N];//二分图权值
int link[MAX_N], l_x[MAX_N], l_y[MAX_N];//y中各店匹配的状态, x,y,中的点标号
int slack[MAX_N];
bool vis_x[MAX_N], vis_y[MAX_N];
bool DFS(int x)
{
vis_x[x] = true;
for(int y = 0; y < n_y; y++)
{
if(vis_y[y])
continue;
int temp = l_x[x] + l_y[y] - G[x][y];
if(temp == 0)
{
vis_y[y] = true;
if(link[y] == -1 || DFS(link[y]))
{
link[y] = x;
return true;
}
}
else if(slack[y] > temp)
slack[y] = temp;
}
return false;
}
int KM()
{
memset(link, -1, sizeof(link));
memset(l_y, 0, sizeof(l_y));
for(int i = 0; i < n_x; i++)
{
l_x[i] = -INF;
for(int j = 0; j < n_y; j++)
if(G[i][j] > l_x[i])
l_x[i] = G[i][j];
}
for(int x = 0; x < n_x; x++)
{
for(int i = 0; i < n_y; i++)
slack[i] = INF;
while(true)
{
memset(vis_x, false, sizeof(vis_x));
memset(vis_y, false, sizeof(vis_y));
if(DFS(x))
break;
int d = INF;
for(int i = 0; i < n_y; i++)
if(!vis_y[i] && d > slack[i])
d = slack[i];
for(int i = 0; i < n_y; i++)
if(vis_x[i])
l_x[i] -= d;
for(int i = 0; i < n_y; i++)
{
if(vis_y[i])
l_y[i] += d;
else
slack[i] -= d;
}
}
}
int res = 0;
for(int i = 0; i < n_y; i++)
if(link[i] != -1)
res += G[link[i]][i];
return res;
}
int main()
{
int n;
while(scanf("%d", &n) != EOF)
{
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
scanf("%d", &G[i][j]);
n_x = n_y = n;
printf("%d\n", KM());
}
return 0;
}
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn = 300 + 10;
const int INF = 0x3f3f3f3f;
int n;
int W[maxn][maxn];
int Lx[maxn], Ly[maxn]; // 顶标
int left[maxn]; // left[i]为右边第i个点的匹配点编号
bool S[maxn], T[maxn]; // S[i]和T[i]为左/右第i个点是否已标记
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] || match(left[j])){
left[j] = i;
return true;
}
}
return false;
}
void update(){
int a = INF;
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;
}
}
void KM() {
for(int i = 1; i <= n; i++) {
left[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++) {
for(;;) {
for(int j = 1; j <= n; j++) S[j] = T[j] = 0;
if(match(i)) break; else update();
}
}
}
int main(){
while(scanf("%d", &n) != EOF) {
int price;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
scanf("%d", &price);
W[i][j] = price;
}
}
KM(); // 最大权匹配
int ans = 0;
for(int i = 1; i <= n; i++)
ans += W[left[i]][i];
printf("%d\n", ans);
}
return 0;
}