poj 3686(费用流+拆很多点)

题目大意:

有n个订单m个车间,每个车间均可以单独完成任何一个订单。每个车间完成不同订单的时间是不同的。不会出现两个车间完成同一个订单的情况。给出每个订单在某个车间完成所用的时间。问订单完成的最小平均时间是多少。



这个题在建图上有一些需要思考很长时间的地方。因为每个订单所消耗的时间是车间完成订单的时间加上订单等待的时间。我们设在车间A需要完成k个订单,消耗的总时间是t1+(t1+t2)+(t1+t2+t3)……转换一下就是t1*k+t2*(k-1)+t3*(k-2)……我们就找到了规律:当第i个订单在第j个车间是倒数第k个任务时,总消耗时间需要加上订单i在车间对应消耗时间的k倍。

将m个车间都拆成n个,形成一个m*n的m行n列矩阵,表示第i个任务在第j个车间的倒数第k个时间段完成,
源点是0,汇点t ,所以, 点的总数是 n +m*n +2 ;  


#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N= 5000 ;
const int M= 800000 ;
const int inf =1<< 30 ;
struct node
{
	int u ,v,c,cost ,next ;
}edge[M] ;
int head[N],pp[N],pre[N],dist[N],vist[N] ;
int top  ;
int cost[100][100];

void add(int u ,int v,int c,int cost)        
{        
    edge[top].u=u;        
    edge[top].v=v;        
    edge[top].c=c;        
    edge[top].cost=cost;        
    edge[top].next=head[u];        
    head[u]=top++;        
    edge[top].u=v;        
    edge[top].v=u;        
    edge[top].c=0;        
    edge[top].cost=-cost;        
    edge[top].next=head[v];        
    head[v]=top++;        
}        
        
int SPFA(int s,int t)        
{        
    int u , v ;        
    memset(vist,0,sizeof(vist));        
    memset(pre,-1,sizeof(pre));        
    for(int i = 0 ; i <= t ; i++)  dist[i]=inf ;        
    vist[s]=1;dist[s]=0;pre[s]=s;        
    queue<int>q;        
    q.push(s);        
    while(!q.empty())        
    {        
         u=q.front();        
         q.pop();        
         vist[u]=0;        
         for(int i =head[u];i!=-1;i=edge[i].next)        
         {        
               v=edge[i].v;        
               if(edge[i].c && dist[v] > dist[u]+edge[i].cost)        
               {        
                    dist[v] = dist[u]+edge[i].cost ;        
                     pre[v]=u;        
                     pp[v]=i;        
                     if(!vist[v]);        
                     {        
                           vist[v]=1;        
                           q.push(v);        
                     }        
               }        
        }        
    }        
    if(dist[t]==inf) return 0;        
    return 1 ;        
}        
        
int MFMC(int s,int t)        
{        
    int mincost=0,flow=0,minflow ;        
    while(SPFA(s,t))        
    {        
          minflow=inf;        
          for(int i=t;i!=s;i=pre[i])        
              minflow=min(minflow,edge[pp[i]].c);        
          for(int i=t;i!=s;i=pre[i])        
          {        
                edge[pp[i]].c -= minflow;        
                edge[pp[i]^1].c += minflow;        
          }            
          flow += minflow;        
          mincost += dist[t]*minflow ;        
        //  printf("****");    
    }        
    return mincost ;        
}        

int main()
{
	  int T ,x,n,m;
	  scanf("%d",&T) ;
	  while(T--)
	  {
	  	      top = 0 ;
	  	      memset(head,-1,sizeof(head)) ;
	  	      memset(cost,0,sizeof(cost)) ;
	  	      scanf("%d%d",&n,&m);
	  	      for(int i = 1 ; i <= n ; i++)
	  	        for(int j = 1 ; j <= m ; j++)
				       scanf("%d",&cost[i][j]) ;
				
				 int s = 0 , t = n+m*n+1    ;
		     for(int  i = 1 ; i <= n ; i++) //i任务 
			    for(int j = 1 ;j <= m ; j++)  //j车间 
			     for(int k = 1 ; k <= n ; k++)//倒数第k个时段完成 ; 
			          add(i,n+(j-1)*n+k,1,cost[i][j]*k) ;

		    for(int i = 1 ; i <= n ; i++)
			    add(s,i,1,0) ;
			
			for(int i = n+1 ; i <= n+m*n ; i++)
			   add(i,t,1,0) ;   
			   int ans = MFMC(s,t) ;
	  	   printf("%.6lf\n",ans*1.0/n) ;
	  }
	return 0 ;
}
 



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值