poj 3469 最小割

题意:现在有n个任务,两个机器A和B,每个任务要么在A上完成,要么在B上完成,而且知道每个任务在A和B机器上完成所需要的费用。然后再给m行,每行a,b,w三个数字。表示如果a任务和b任务不在同一个机器上工作的话,需要额外花费w。现在要求出完成所有任务最小的花费是多少。


1:每个任务看成一个顶点,就只有n+2的顶点,然后源点连接到每个任务顶点一条边,权值为这个任务在A机器上所要的花费,每个任务顶点连接到汇点一条边,权值为这个任务在B机器上所要的花费。一开始以A和B机器做为顶点,结果也是正确的,但这样顶点就是2*n+2个,TLE了。

思路: 如果将两个机器分别视为源点和汇点、任务视为顶点,则可以按照以下方式构图:对于第i
个任务在每个机器中的耗费Ai和Bi, 从源点向顶点i连接一条容量为Ai的弧、从顶点i向汇点
连接一条容量为Bi的弧;对于a任务与b任务在不同机器中运行造成的额外耗费w,顶点a
与顶点b连接一条容量为w的弧(双向边,因为消耗是对双方而言)。此时每个顶点(任务)都和源点及汇点(两个机器)相连,即
每个任务都可以在任意一个机器中运行
不难了解到,对于图中的任意一个割,源点与汇点必不连通。因此每个顶点(模块)都不可
能同时和源点及汇点(两个机器)相连,即每个模块只在同一个机器中运行。此时耗费即为割
的容量。很显然,当割的容量取得最小值时,总耗费最小。故题目转化为求最小割的容量。


#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=22222;
const int M=1000000;
const int inf=1<<30 ;
struct node
{
	int u,v,c,next;
}edge[M];
int head[N],pre[N],dis[N],cur[N],gap[N];
int top ;

void add(int u ,int v,int c)             
{            
    edge[top].u=u;            
    edge[top].v=v;            
    edge[top].c=c;            
    edge[top].next=head[u];            
    head[u]=top++;            
    edge[top].u=v;            
    edge[top].v=u;            
    edge[top].c=0;            
    edge[top].next=head[v];            
    head[v]=top++;            
}        
int sap(int s,int t ,int nv)          
{          
    memset(dis,0,sizeof(dis));          
    memset(gap,0,sizeof(gap));          
    int u,v,minflow=inf,flow=0;          
    for(int i = 0 ; i <nv ; i++)  cur[i]=head[i] ;          
    u=pre[s]=s;          
    gap[s]=nv;          
    while(dis[s] < nv )          
    {          
            loop :          
               for(int &j=cur[u] ; j!=-1 ; j=edge[j].next)                         
               {          
                     v=edge[j].v ;          
                     if(edge[j].c > 0 && dis[u] == dis[v] +1 )          
                     {          
                              minflow = min(minflow ,edge[j].c) ;          
                              pre[v]=u;          
                              u=v ;          
                          if(v==t)          
                          {            
                                for( u =pre[v] ; v!=s ;v=u,u=pre[u])          
                                      {          
                                            edge[cur[u]].c -= minflow ;           
                                            edge[cur[u]^1].c += minflow ;          
                                      }           
                                flow += minflow ;          
                                minflow = inf ;          
                          }          
                         goto loop ;          
                     }          
               }          
           int mindis=nv ;          
           for(int i = head[u] ; i!=-1 ; i=edge[i].next)          
           {          
                  v=edge[i].v ;          
                  if(edge[i].c > 0 && dis[v] < mindis)          
                  {          
                       mindis = dis[v] ;          
                       cur[u]=i ;                 
                 }          
           }          
          if(--gap[dis[u]]==0) break ;          
          gap[ dis[u] = mindis +1 ]++  ;          
          u=pre[u] ;          
    }          
            return flow ;
}        

int main()
{
	  int n,m,x,y,a,b,w;
	  while(~scanf("%d%d",&n,&m))
	  {
	  	     top= 0;
	  	     memset(head,-1,sizeof(head));
	  	     int s=0,t=n+1 ;
	  	     for(int i = 1 ; i <= n ; i++)
	  	     {
	  	     	     scanf("%d%d",&x,&y);
	  	     	     add(s,i,x);
	  	     	     add(i,t,y);
	  	     }
	  	     for(int i = 1 ; i <= m ; i++)
	  	     {
	  	     	    scanf("%d%d%d",&a,&b,&w);
	  	     	    add(a,b,w);
	  	     	    add(b,a,w);
	  	     }
	  	    int ans=sap(s,t,t+1);
	  	    printf("%d\n",ans);
	  }
	return 0;
}  


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值