POJ 2516 最小费用流构K次图

【题意】:有n个商店,m个供应商,k种商品,给出每个商店对每种商品的需求,每个供应商对于每种商品的库存,和对于每种商品,每个供应商运到每个商店的费用。
              问:能否满足每个商店的需求,如果能,输出最少的费用;否则输出-1。

【题解】:这是一场个人赛的题目,当时一开始没反应过来是最小费用流,到我意识到的时候已经没有时间敲代码了。
              其实这题就是好裸的最小费用流,只需要注意一点,对于每一种商品各自构一次图,跑最小费用流即可。至今不明白为什么k个图没联系,弄在一起跑费用流会超时。

              对于每个构图就是 s->供应商->商店->t,最后判断一下最大流是否等于各个商店的需求和即可。

算法一次敲对了,不过已经判断出错误就没继续输入了,坑爹啊

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

const int n_max=105;
const int m_max=10005;
const int inf=1<<30;
struct edge
{
	int u,v,cap,cost,pre;
	void add(int pu,int pv,int pcap,int pcost,int ppre)
	{
		u=pu;
		v=pv;
		cap=pcap;
		cost=pcost;
		pre=ppre;
	}
}e[m_max];
int ne;
int head[n_max];
int sup[n_max][n_max],need[n_max][n_max];
int min_cost,max_flow;

void init()
{
	ne=0;
	memset(head,-1,sizeof(head));
}

void addedge(int u,int v,int cap,int cost)
{
	e[ne].add(u,v,cap,cost,head[u]);
	head[u]=ne++;
	e[ne].add(v,u,0,-cost,head[v]);
	head[v]=ne++;
}

void min_cost_max_flow(int s,int t)
{
	min_cost=max_flow=0;
	while(true)
	{
		int p[n_max];
		memset(p,-1,sizeof(p));
		int d[n_max];
		int i;
		for(i=0;i<n_max;i++)
		{
			d[i]=inf;
		}
		d[s]=0;
		bool inq[n_max];
		memset(inq,0,sizeof(inq));
		inq[s]=true;
		queue<int> q;
		q.push(s);
		int u,v;
		while(!q.empty())
		{
			u=q.front();
			q.pop();
			inq[u]=false;
			for(i=head[u];i!=-1;i=e[i].pre)
			{
				v=e[i].v;
				if(e[i].cap&&d[v]>d[u]+e[i].cost)
				{
					d[v]=d[u]+e[i].cost;
					p[v]=i;
					if(!inq[v])
					{
						q.push(v);
						inq[v]=true;
					}
				}
			}
		}
		if(d[t]==inf)
		{
			break;
		}
		int nmin=inf;
		for(i=p[t];i!=-1;i=p[e[i].u])
		{
			nmin=min(nmin,e[i].cap);
		}
		for(i=p[t];i!=-1;i=p[e[i].u])
		{
			e[i].cap -= nmin;
			e[i^1].cap += nmin;
		}
		min_cost += d[t]*nmin;
		max_flow += nmin;
	}
}

int main()
{
	int n,m,k,s,t;
	while(~scanf("%d%d%d",&n,&m,&k))
	{
		if(n+m+k==0)
		{
			break;
		}
		int i,j,l;
		s=0;
		t=m+n+1;
		for(i=1;i<=n;i++)
		{
			for(j=1;j<=k;j++)
			{
				scanf("%d",&need[i][j]);
			}
		}
		for(i=1;i<=m;i++)
		{
			for(j=1;j<=k;j++)
			{
				scanf("%d",&sup[i][j]);
			}
		}
		bool bans=true;
		int sum_cost=0;
		int x;
		for(l=1;l<=k;l++)
		{
			init();
			for(i=1;i<=n;i++)
			{
				for(j=1;j<=m;j++)
				{
					scanf("%d",&x);
					addedge(j,m+i,inf,x);
				}
			}
			if(!bans)
			{
				continue;
			}
			for(i=1;i<=m;i++)
			{
				if(sup[i][l])
				{
			    	addedge(s,i,sup[i][l],0);
				}
			}
			int needall=0;
			for(i=1;i<=n;i++)
			{
				if(need[i][l])
				{
					needall+=need[i][l];
					addedge(m+i,t,need[i][l],0);
				}
			}
			min_cost_max_flow(s,t);
			if(max_flow==needall)
			{
				sum_cost+=min_cost;
			}
			else
			{
				bans=false;
			}
		}
		if(bans)
		{
			printf("%d\n",sum_cost);
		}
		else
		{
			printf("-1\n");
		}
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值