bzoj1497

要求最大获利

如果我能使损失的收益最小,那么就是最大获利了。

把每一个中转站看成一个点,用户群也看成一个点

源向中转站连边,容量为建中转站的费用

用户群向汇连边,容量为收益

如果用户群i需要j和k中转站,那么j,k向i连边,容量为无限大。

求出最小割

源到站j,k,用户i到汇这三条边是不会同时割掉的,因为不可能同时损失这两者的收益

为什么

因为他们中间有容量无限大的边,这边是不会割的,所以只有可能割两端

#include<cstdio>
#include<cstdlib>
#include<cstring>
struct mod{int x,y,c,next,other;};
mod q[400005];
int first[100005];
int h[100005];
int t[100005];
int len=0,S,T;
void ins(int x,int y,int c)
{
 len++;
 q[len].x=x;
 q[len].y=y;
 q[len].c=c;
 q[len].next=first[x];
 first[x]=len;
 q[len].other=len+1;
  
 len++;
 q[len].x=y;
 q[len].y=x;
 q[len].c=0;
 q[len].next=first[y];
 first[y]=len;
 q[len].other=len-1;    
}
int mymin(int u1,int u2)
{
 if (u1<u2)return u1;
 return u2; 
}
bool set_h()
{
 int st=1,ed=2;
 memset(h,0,sizeof(h));
 h[S]=1;
 t[1]=S;
 while(st<ed)
 {
  int x=t[st];
  for (int i=first[x];i!=-1;i=q[i].next)
  {
   int y=q[i].y;
   if (q[i].c>0&&h[y]==0)
   {
    h[y]=h[x]+1;
    t[ed]=y;
    ed++;
   }
  }
  st++;
 }
 if (h[T]==0)return false;
 return true;
}
int findf(int x,int f)
{
    if (x==T)return f;
    int s=0;
    for (int i=first[x];i!=-1;i=q[i].next)
    {
     int y=q[i].y;
     if (q[i].c>0&&h[y]==h[x]+1&&s<f)
     {
      int o=findf(y,mymin(f-s,q[i].c));
      s+=o;
      q[i].c-=o;
      q[q[i].other].c+=o;
     }
    }
    if (s==0)h[x]=0;
    return s;
}
int main()
{
 int n,m;
 scanf("%d%d",&n,&m);
 T=n+m+1;
 memset(first,-1,sizeof(first));
 len=0;
 int ans=0,num=0;
 for (int i=1;i<=n;i++)
 {
  int p;
  scanf("%d",&p);
  ins(S,i,p);   
 }
 for (int i=1;i<=m;i++)
 {
  int A,B,C;
  scanf("%d%d%d",&A,&B,&C);
  ins(A,i+n,999999999);
  ins(B,i+n,999999999);
  ins(i+n,T,C); 
  ans+=C;
 }
 while(set_h()==true)
 {
  num+=findf(S,999999999);  
 }
 printf("%d\n",ans-num);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值