当前位置: 代码迷 >> 综合 >> POJ 2516 Minimum Cost KM算法 或者 最小费用最大流
  详细解决方案

POJ 2516 Minimum Cost KM算法 或者 最小费用最大流

热度:13   发布时间:2024-01-13 17:52:39.0

这题还是挺裸的

当然 是用最小费最为方便。建图一目了然


KM麻烦那么一点,就是每个物品要拆成一份一份的。

建图的时候注意,KM匹配的模板一定是左边的点数不大于右边的点数  至少我的模板是这样的

在本题中,要求供应商提供的东西要满足商店的需求,不能满足就直接输出-1, 所以理论上商店的点应该比供应商少,商店的点应该放在左边,供应商放在右边

由于是最小权匹配,所以我这里用了一个比较大的数减去权值来处理,而不是直接取负数


先是一个最小费的

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#define eps 1e-5
#define MAXN 111
#define MAXM 55555
#define INF 100000007
using namespace std;
struct EDGE
{int v, cap, cost, next, re;    //  re记录逆边的下标。
} edge[MAXM];
int n, m, ans, flow, src, des;
int e, head[MAXN];
int que[MAXN], pre[MAXN], dis[MAXN];
bool vis[MAXN];
void init()
{e = ans = flow = 0;memset(head, -1, sizeof(head));
}
void add(int u, int v, int cap, int cost)
{edge[e].v = v;edge[e].cap = cap;edge[e].cost = cost;edge[e].next = head[u];edge[e].re = e + 1;head[u] = e++;edge[e].v = u;edge[e].cap = 0;edge[e].cost = -cost;edge[e].next = head[v];edge[e].re = e - 1;head[v] = e++;
}
bool spfa()
{int i, h = 0, t = 1;for(i = 0; i <= n; i ++){dis[i] = INF;vis[i] = false;}dis[src] = 0;que[0] = src;vis[src] = true;while(t != h){int u = que[h++];h %= n;vis[u] = false;for(i = head[u]; i != -1; i = edge[i].next){int v = edge[i].v;if(edge[i].cap && dis[v] > dis[u] + edge[i].cost){dis[v] = dis[u] + edge[i].cost;pre[v] = i;if(!vis[v]){vis[v] = true;que[t++] = v;t %= n;}}}}if(dis[des] == INF) return false;return true;
}
void end()
{int u, p, mi = INF;for(u = des; u != src; u = edge[edge[p].re].v){p = pre[u];mi = min(mi, edge[p].cap);}for(u = des; u != src; u = edge[edge[p].re].v){p = pre[u];edge[p].cap -= mi;edge[edge[p].re].cap += mi;ans += mi * edge[p].cost;     //  cost记录的为单位流量费用,必须得乘以流量。}flow += mi;
}
int nt, k;
int shop[55][55], sup[55][55];
int money;
int main()
{while(scanf("%d%d%d", &nt, &m, &k) != EOF){if(!nt && !m && !k) break;int res = 0;n = nt + m + 2;src = nt + m + 1;des = nt + m + 2;for(int i = 1; i <= nt; i++)for(int j = 1; j <= k; j++)scanf("%d", &shop[i][j]);for(int i = 1; i <= m; i++)for(int j = 1; j <= k; j++)scanf("%d", &sup[i][j]);int flag = 1;for(int q = 1; q <= k; q++){init();for(int i = 1; i <= nt; i++)for(int j = 1; j <= m; j++){scanf("%d", &money);add(j, i + m, INF, money);}int fk = 0;for(int i = 1; i <= m; i++)add(src, i, sup[i][q], 0);for(int i = 1; i <= nt; i++)add(i + m, des, shop[i][q], 0), fk += shop[i][q];while(spfa()) end();res += ans;if(flow != fk) flag = 0;}if(flag)printf("%d\n", res);else printf("-1\n");}return 0;
}


然后用KM做的

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#define eps 1e-5
#define MAXN 555
#define MAXM 55555
#define INF 100000007
using namespace std;
int n, m, ny, nx;
int w[MAXN][MAXN];
int lx[MAXN], ly[MAXN];
int linky[MAXN];
int visx[MAXN], visy[MAXN];
int slack[MAXN];
bool find(int x)
{visx[x] = 1;for(int y = 1; y <= ny; y++){if(visy[y]) continue;int t = lx[x] + ly[y] - w[x][y];if(t == 0){visy[y] = 1;if(linky[y] == -1 || find(linky[y])){linky[y] = x;return true;}}else if(slack[y] > t) slack[y] = t;}return false;
}
int KM()
{memset(linky, -1, sizeof(linky));for(int i = 1; i <= nx; i++) lx[i] = -INF;memset(ly, 0, sizeof(ly));for(int i = 1; i <= nx; i++)for(int j = 1; j <= ny; j++)if(w[i][j] > lx[i]) lx[i] = w[i][j];for(int x = 1; x <= nx; x++){for(int i = 1; i <= ny; i++) slack[i] = INF;while(true){memset(visx, 0, sizeof(visx));memset(visy, 0, sizeof(visy));if(find(x)) break;int d = INF;for(int i = 1; i <= ny; i++)if(!visy[i]) d = min(d, slack[i]);if(d == INF) return -1;for(int i = 1; i <= nx; i++)if(visx[i]) lx[i] -=d;for(int i = 1; i <= ny; i++)if(visy[i]) ly[i] += d;else slack[i] -= d;}}int cnt = 0;for(int i = 1; i <= ny; i++)if(linky[i] != -1) cnt++;if(cnt != nx) return -1;int tp = 0;for(int i = 1; i <= ny; i++)if(linky[i] != -1 ) tp += w[linky[i]][i] - 200;return -tp;
}
int nt, k;
int shop[55][55], sup[55][55];
int money[55][55];
int ha[555], hb[555];
int main()
{while(scanf("%d%d%d", &nt, &m, &k) != EOF){if(!nt && !m && !k) break;for(int i = 1; i <= nt; i++)for(int j = 1; j <= k; j++)scanf("%d", &shop[i][j]);for(int i = 1; i <= m; i++)for(int j = 1; j <= k; j++)scanf("%d", &sup[i][j]);int flag = 1, res = 0;for(int q = 1; q <= k; q++){memset(w, 0, sizeof(w));for(int i = 1; i <= nt; i++)for(int j = 1; j <= m; j++)scanf("%d", &money[i][j]);nx = 0, ny = 0;for(int i = 1; i <= nt; i++)for(int j = 1; j <= shop[i][q]; j++){nx++;ha[nx] = i;}for(int i = 1; i <= m; i++)for(int j = 1; j <= sup[i][q]; j++){ny++;hb[ny] = i;}for(int i = 1; i <= nx; i++)for(int j = 1; j <= ny; j++)w[i][j] = 200 - money[ha[i]][hb[j]];int tp = KM();if(tp == -1) flag= 0;else res += tp;}if(flag)printf("%d\n", res);else printf("-1\n");}return 0;
}


  相关解决方案