zoj 3229 (有上下届的最大流)

题意:

一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝给给定的C个女神拍照,每天拍照数不能超过D张,而且给每个女神i拍照有数量限制[Li,Ri],对于每个女神n天的拍照总和不能少于Gi,如果有解求屌丝最多能拍多少张照,并求每天给对应女神拍多少张照;否则输出-1。

分析:

增设一源点st,汇点sd,st到第i天连一条上界为Di下界为0的边,每个女神到汇点连一条下界为Gi上界为oo的边,对于每一天,当天到第i个女孩连一条[Li,Ri]的边。

 

建图模型:源点s,终点d。超级源点ss,超级终点dd。首先判断是否存在满足所有边上下界的可行流,方法可以转化成无源汇有上下界的可行流问题。怎么转换呢?

增设一条从d到s没有下界容量,上界容量为无穷的边,那么原图就变成了一个无源汇的循环流图。接下来的事情一样,超级源点ss连i(du[i]>0),i连超级汇点(du[i]<0),

对(ss,dd)进行一次最大流,当maxflow等于所有(du[]>0)之和时,有可行流,否则没有。

当有可行流时,再对(s,d)进行一次最大流,此时得到的maxflow则为题目的解。

为什么呢?因为第一次maxflow()只是求得所有满足下界的流量,而残留网络(s,d)路上还有许多自由流(没有和超级源点和超级汇点连接的边)没有流满,所有最终得到的maxflow=(第一次流满下界的流+第二次能流通的自由流)。


#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=2000;
const int M=1000000;
const int inf=1<<30;
struct node
{
	int u,v,c,next;
}edge[M];
int head[N],gap[N],pre[N],cur[N],dis[N];
int d[N],low[555][1111],flag[555][1111],top ;
int s,t;
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 BFS()  
{  
    memset(dis,-1,sizeof(dis));  
    queue<int>q ;  
    q.push(s);   
    dis[s]=0;  
    while(!q.empty())  
    {  
          int u = q.front();  
          q.pop();  
          for(int i = head[u] ; i!=-1 ; i=edge[i].next)  
          {  
               int v =edge[i].v ;  
               if(edge[i].c >0 && dis[v]==-1)  
               {  
                      dis[v]=dis[u]+1;  
                      q.push(v);  
               }  
              
          }  
    }  
    if(dis[t]==-1)  
      return 0;  
    return 1;    
}   
  
int DFS(int u ,int minx)  
{  
    if(u==t)  return minx ;  
    int tmp ;  
    for(int i = head[u];i!=-1 ; i=edge[i].next)  
    {    
        int v=edge[i].v;  
        if(edge[i].c >0 && dis[v]==dis[u]+1 && (tmp=DFS(v,min(minx,edge[i].c))))   
        {  
                edge[i].c -= tmp;  
                edge[i^1].c+=tmp;  
                return tmp ;  
        }  
    }           
    dis[u]=-1; 
    return 0;     
}  
  
int Dinic()  
{  
    int ans= 0 , tmp;  
    while(BFS())    //找层次图 ,直到不能找到   
    {  
          while(1)  
          {  
                 tmp = DFS(s,inf);    
                   if(tmp==0) break;  
                 ans += tmp ;      
              
          }  
    }  
    return ans ;  
}  

int main()
{
	  int n,m,x,C,T,u,l,h;
	  while(~scanf("%d%d",&n,&m))
	  {
	  	     top = 0 ;
	  	    memset(head,-1,sizeof(head));
	  	    memset(d,0,sizeof(d));
	  	    memset(low,0,sizeof(low));
	  	    memset(flag,0,sizeof(flag));
	  	     s=0,t=n+m+1; 
			for(int i = 1 ; i <= m ; i++)
             {
               scanf("%d",&x);
               d[n+i] -= x;
               add(n+i,t,inf-x);
               d[t] += x;
		     }
	  	     for(int i = 1 ; i <= n ; i++)
	  	     {
	  	     	  scanf("%d%d",&C,&T);
				  add(s,i,T);
				  while(C--)
				  {	   
				  	   scanf("%d%d%d",&u,&l,&h);
				  	   u++;
					   add(i,n+u,h-l); 
				  	   d[i] -= l ;
				  	   d[u+n] += l ;
				  	   low[i][u]=l;
				  	   flag[i][u]=top-2;
				  }	  
	  	     }
	  	    int ss = t+1 ,tt=t+2 , sum= 0 ;
			for(int i = 1 ; i <= t ;i++)
			{
				if(d[i] > 0)
				  {
				      add(ss,i,d[i]);
				      sum += d[i] ;
				  }
				else if(d[i] < 0)
				     add(i,tt,-d[i]) ;     
			}  
		
	  	   add(t,s,inf);
	  	   s=ss;t=tt;
	  	   int maxflow = Dinic();
	  	   if(maxflow == sum)
	  	   {      s=0,t=n+m+1;
	  	   	       int ans = Dinic();
	  	   	       printf("%d\n",ans);
	  	   	       for(int i = 1 ; i <= n ; i++)
	  	   	        for(int j = 1 ; j <= m ; j++)
	  	   	         {
	  	   	         	   if(flag[i][j])
	  	   	         	  printf("%d\n",edge[flag[i][j]^1].c+low[i][j]);
	  	   	         }
	  	   }else
	  	      printf("-1\n");
	  	      puts("");
	  }
	return 0;
}



 




 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值