这题还是挺裸的
当然 是用最小费最为方便。建图一目了然
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;
}
分享到:
相关推荐
北大POJ2516-Minimum Cost 解题报告+AC代码 http://blog.csdn.net/lyy289065406/article/details/6742534
poj2516代码最小费用最大流
【二分图顶点覆盖->最小割->最大流->Dinic算法求解】 解题报告+AC代码 http://hi.csdn.net/!s/WKVPR0 ----> 我的所有POJ解题报告 http://blog.csdn.net/lyy289065406/article/details/6642573
北大POJ2195-Going Home【费用流】 解题报告+AC代码 http://blog.csdn.net/lyy289065406/article/details/6732762
POJ题目分类,列出了所有的类目,里面写了一些很好的框架。
北大POJ初级-图算法 解题报告+AC代码
北大POJ初级-基本算法 解题报告+AC代码
解决算法问题 poj1082, poj1150, poj1180, poj1201, poj1222,代码完成所给题目要求。
POJ各题算法分类和题目推荐 ACM必看 POJ各题算法分类和题目推荐 ACM必看 POJ各题算法分类和题目推荐 ACM必看
poj上的算法题目分类,对于大家想练习算法的同鞋可以参考一下,里面按类列出了各种算法的题号。
poj acm题解,包括绝大部分poj题目的题解,可以供acm爱好者学习研究
NULL 博文链接:https://128kj.iteye.com/blog/1705139
北大POJ中级-基本算法 解题报告+AC代码
关于C++ 算法 北大网站POJ 八数码问题
用贪心算法解决POJ 1065的木棍处理问题
这里面有介绍ACM中的算法,包括算法分类,以及对应在POJ上面的训练题目
poj1087贪心算法实验报告 poj1087贪心算法实验报告
POJ分类POJ分类POJ分类POJ分类POJ分类POJ分类POJ分类POJ分类POJ分类POJ分类POJ分类POJ分类POJ分类POJ分类POJ分类POJ分类POJ分类POJ分类
POJ中级图算法所有题目【解题报告+AC代码】 我的所有POJ解题报告 http://blog.csdn.net/lyy289065406/article/details/6642573
初期: 一.基本算法: (1)枚举. (poj1753,poj2965) (2)贪心(poj1328,poj2109,poj2586) (3)递归和分治法.... (4)递推.... (5)构造法.(poj3295) ... (6)最大流的增广路算法(KM算法). (poj1459,poj3436) ......