(最小费用最大流)POJ2516 Minimum Cost
2015-07-17 19:42
369 查看
很经典的一道费用流问题,题目大意是有N个商店,M个供应商,供应K种物品,每家商店对每种物品都有一个需求量,每家供应商供应的每种物品的量不同,而每家供应商运送每种物品到一家商店的成本也不一样,求在供等于求和供大于求的情况下供应商满足所有商店需求的最小成本,若供不应求,输出-1。
建图方法如下:
1.建立一个超级源,将其与供应商连接起来,容量为其对一种物品的供应量,代价为0;
2.建立一个超级汇,将其与商店连接起来,容量为商店对一种物品的需求量,代价也为0;
3.供应商与商店之间的边容量为无穷大,代价即供应商运送该物品到商店的成本。
有K种物品,所以要做K次费用流。
判断供不应求的方法:
很简单,每次完成费用流计算后,扫描一次超级汇和商店之间的所有边,若有容量剩余(商店的对某种物品需求量没有得到满足),即供不应求。判断出一次供不应求的情况后,之后的物品只需更新网络容量和代价即可,不需计算费用流,以节省时间。
最小费用流只是在最大流的基础上,在寻找增广路的过程中将增广路的寻找条件变成了寻找源点到汇点的最短路。由于有负权边的存在(这是无法避免的),所以只能采用Bellman-Ford,或其改进算法SPFA,本题采用SPFA。
以下是详细代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define INF 20000000
bool vis[105];
int cnt,n,m,result;
int d[105],pre[105],cost[105][105],cap[105][105];
int order[55][55],store[55][55];
queue <int> q;
bool spfa(){
for (int i=1;i<cnt;i++){
d[i]=INF;
vis[i]=false;
}
d[0]=0;
while (!q.empty())
q.pop();
q.push(0);
while (!q.empty()){
int u=q.front();
q.pop();
vis[u]=true;
for (int i=1;i<cnt;i++)
if (cap[u][i] && d[i]>d[u]+cost[u][i]){
d[i]=d[u]+cost[u][i];
pre[i]=u;
if (!vis[i]){
vis[i]=true;
q.push(i);
}
}
vis[u]=false;
}
if (d[1]<INF)
return true;
return false;
}
void costflow(){
int flow=INF;
for (int i=1;i!=0;i=pre[i])
flow=min(flow,cap[pre[i]][i]);
for (int i=1;i!=0;i=pre[i]){
cap[pre[i]][i]-=flow;
cap[i][pre[i]]+=flow;
result+=cost[pre[i]][i]*flow;
}
}
int main(){
int k;
bool flag;
while (scanf("%d%d%d",&n,&m,&k) && (n||m||k)){
flag=true;
cnt=n+m+2;
for (int i=0;i<n;i++)
for (int j=0;j<k;j++)
scanf("%d",&order[i][j]);
for (int i=0;i<m;i++)
for (int j=0;j<k;j++)
scanf("%d",&store[i][j]);
result=0;
for (int t=0;t<k;t++){
memset(cap,0,sizeof(cap));
memset(pre,-1,sizeof(pre));
for (int i=0;i<m;i++){
cap[0][i+2]=store[i][t];
cost[0][i+2]=0;
}
for (int i=0;i<n;i++){
cap[i+m+2][1]=order[i][t];
cost[i+m+2][1]=0;
}
for (int i=0;i<n;i++){
for (int j=0;j<m;j++){
scanf("%d",&cost[2+j][2+m+i]);
cap[2+j][2+m+i]=INF;
cost[2+m+i][2+j]=-cost[2+j][2+m+i];
}
}
if (!flag)
continue;
while (spfa())
costflow();
for (int i=0;i<n;i++){
if (cap[i+m+2][1]>0){
flag=false;
break;
}
}
}
if (flag)
printf("%d\n",result);
else
printf("-1\n");
}
return 0;
}
建图方法如下:
1.建立一个超级源,将其与供应商连接起来,容量为其对一种物品的供应量,代价为0;
2.建立一个超级汇,将其与商店连接起来,容量为商店对一种物品的需求量,代价也为0;
3.供应商与商店之间的边容量为无穷大,代价即供应商运送该物品到商店的成本。
有K种物品,所以要做K次费用流。
判断供不应求的方法:
很简单,每次完成费用流计算后,扫描一次超级汇和商店之间的所有边,若有容量剩余(商店的对某种物品需求量没有得到满足),即供不应求。判断出一次供不应求的情况后,之后的物品只需更新网络容量和代价即可,不需计算费用流,以节省时间。
最小费用流只是在最大流的基础上,在寻找增广路的过程中将增广路的寻找条件变成了寻找源点到汇点的最短路。由于有负权边的存在(这是无法避免的),所以只能采用Bellman-Ford,或其改进算法SPFA,本题采用SPFA。
以下是详细代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define INF 20000000
bool vis[105];
int cnt,n,m,result;
int d[105],pre[105],cost[105][105],cap[105][105];
int order[55][55],store[55][55];
queue <int> q;
bool spfa(){
for (int i=1;i<cnt;i++){
d[i]=INF;
vis[i]=false;
}
d[0]=0;
while (!q.empty())
q.pop();
q.push(0);
while (!q.empty()){
int u=q.front();
q.pop();
vis[u]=true;
for (int i=1;i<cnt;i++)
if (cap[u][i] && d[i]>d[u]+cost[u][i]){
d[i]=d[u]+cost[u][i];
pre[i]=u;
if (!vis[i]){
vis[i]=true;
q.push(i);
}
}
vis[u]=false;
}
if (d[1]<INF)
return true;
return false;
}
void costflow(){
int flow=INF;
for (int i=1;i!=0;i=pre[i])
flow=min(flow,cap[pre[i]][i]);
for (int i=1;i!=0;i=pre[i]){
cap[pre[i]][i]-=flow;
cap[i][pre[i]]+=flow;
result+=cost[pre[i]][i]*flow;
}
}
int main(){
int k;
bool flag;
while (scanf("%d%d%d",&n,&m,&k) && (n||m||k)){
flag=true;
cnt=n+m+2;
for (int i=0;i<n;i++)
for (int j=0;j<k;j++)
scanf("%d",&order[i][j]);
for (int i=0;i<m;i++)
for (int j=0;j<k;j++)
scanf("%d",&store[i][j]);
result=0;
for (int t=0;t<k;t++){
memset(cap,0,sizeof(cap));
memset(pre,-1,sizeof(pre));
for (int i=0;i<m;i++){
cap[0][i+2]=store[i][t];
cost[0][i+2]=0;
}
for (int i=0;i<n;i++){
cap[i+m+2][1]=order[i][t];
cost[i+m+2][1]=0;
}
for (int i=0;i<n;i++){
for (int j=0;j<m;j++){
scanf("%d",&cost[2+j][2+m+i]);
cap[2+j][2+m+i]=INF;
cost[2+m+i][2+j]=-cost[2+j][2+m+i];
}
}
if (!flag)
continue;
while (spfa())
costflow();
for (int i=0;i<n;i++){
if (cap[i+m+2][1]>0){
flag=false;
break;
}
}
}
if (flag)
printf("%d\n",result);
else
printf("-1\n");
}
return 0;
}
相关文章推荐
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法
- C#折半插入排序算法实现方法
- 基于C++实现的各种内部排序算法汇总
- C++线性时间的排序算法分析
- C++实现汉诺塔算法经典实例
- PHP实现克鲁斯卡尔算法实例解析
- C#常见算法面试题小结