poj2516 最小费用最大流

来源:互联网 发布:阿里云 青岛 编辑:程序博客网 时间:2024/04/29 11:32
#include "stdio.h"  // 最小费用最大流 poj 2516#include "string.h"#include "queue"using namespace std;#define N 115#define INF 1000000000struct node {int u,v,w,k;int next;}edge[8*N*N];int store[N];int n,m,k,ans,idx;int head[N],dis[N],route[N],mark[N];int shop[N][N],supply[N][N],cost[N][N];void init();void EK(int start,int end);  int SPFA(int start,int end);  void adde(int u,int v,int w,int k);  void addedge(int u,int v,int w,int k);  int main(){int sum;int i,j,tt;int sum1[N],sum2[N];while(scanf("%d %d %d",&n,&m,&k)){tt=k;if(n==0 && m==0 && k==0)return 0;memset(sum1,0,sizeof(sum1));memset(sum2,0,sizeof(sum2));for(i=1;i<=n;i++){for(j=1;j<=k;j++){scanf("%d",&shop[i][j]);sum1[j]+=shop[i][j];  //N家商店需要第j件物品的总件数}}for(i=1;i<=m;i++){for(j=1;j<=k;j++){scanf("%d",&supply[i][j]);sum2[j]+=supply[i][j];   //M处仓库供应第j件物品的总件数}}sum = 0;   //答案初始化(sum记录结果)bool flag=true;for(tt=1;tt<=k;tt++){if(sum1[tt]>sum2[tt]) flag=false;init();int start=0,end=n+m+1;for(i=1;i<=n;i++){for(j=1;j<=m;j++){scanf("%d",&cost[i][j]);adde(j,i+m,cost[i][j],INF);}}for(j=1;j<=m;j++)adde(start,j,0,supply[j][tt]);for(i=1;i<=n;i++){store[i] = idx;adde(i+m,end,0,shop[i][tt]);}while(SPFA(start,end))EK(start,end);sum += ans;}if(flag==false)printf("-1\n");elseprintf("%d\n",sum);}return 0;}void init(){ans = 0;idx = 0;memset(head,-1,sizeof(head));}void adde(int u,int v,int w,int k)  //对其中的一条再加上一条流量为0的回路  {      addedge(u,v,w,k);      addedge(v,u,-w,0);  }    void addedge(int u,int v,int w,int k)  //邻接表建边  {      edge[idx].u = u;      edge[idx].v = v;      edge[idx].w = w;      edge[idx].k = k;      edge[idx].next = head[u];      head[u] = idx;      idx++;  }  int SPFA(int start,int end)  //找一条存在流量的最小费用流(存在流量就行)  {      int i;      memset(mark,false,sizeof(mark));  //初始化标记数组mark[];      memset(route,-1,sizeof(route));   //ruote[]记录流量路径(存下一条边的下标)      for(i=start;i<=end;i++)      dis[i] = INF;      dis[start] = 0;      queue<int> q;      q.push(start);      mark[start] = true;      int x,y;      while(!q.empty())      {          x = q.front();          for(i=head[x];i!=-1;i=edge[i].next)          {              y = edge[i].v;              if(edge[i].k && dis[y] > dis[x] + edge[i].w)              {                  dis[y] = dis[x] + edge[i].w;                  route[y] = i;   //route[]里面存的为边的下标                  if(mark[y] == false)                  {                       mark[y] = true;                      q.push(y);                  }              }          }          q.pop();          mark[x] = false;  //对出队列的点的标记还原      }      if(dis[end] == INF) return 0;      return 1;  }    void EK(int start,int end)  {  int s=INF;    int x,y;  y = route[end];while(y!=-1){if(s>edge[y].k)s = edge[y].k;y = route[edge[y].u];}    y = route[end];      while(y!=-1)      {          x = y^1;  //很特别的处理;          edge[y].k -= s;  //对流量进行处理(正减反加)          edge[x].k += s;          ans += edge[y].w*s;          y = route[edge[y].u]; //通过route[]访问路径上的下一个节点      }}