hdu 3879 Base Station (最大权闭合图)

本文介绍了一种解决最大获利问题的方法,通过构建网络流模型并利用最小割算法找到最优解。具体步骤包括将问题转化为最大权闭合图模型,建立源点到获利点、花费点到汇点的边,并通过求最小割获得最终答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

结论:正的权值的和-建图后的最小割的容量

 选择了一条边就会选择两个点,边的花费为正,点的花费为负,把边看成点,这个点向两个端点连一条边,表示选择这条边就会选择这两个点

然后题目就相当于最大权闭合图的模型了

题意:有n个点,m个选择,建造n个点各自需要一定花费,每个选择有一定的获利,会选择两个点,当然也要花费

求最大的获利

每个选择看成是获利点,每个点看成是花费点,新建源点向获利点建边,权值为获利的大小,花费点向汇点建边,权值为花费的大小

每个选择向相应的两个点连一条容量为无穷大的边,然后求网络的最小割

答案就为正的权值和-最小割的容量

最小割肯定为简单割(直接与源点或汇点相连),在这个题目中

如果与汇点相连的边为割边,表示这个点没有被选择来建station

如果与源点相连的边为割边,表示不选择某个客户的要求来连接某两个station

所以可以理解成:最终收益所有可能收益-(损失的收益+架设费用)括号中的即为最小割所求

 

//要加反向边模板

#include<iostream>
 #include<stdio.h>
 #include<memory.h>
 #include<cmath>
 using namespace std;  
 #define MAXN 60000
 #define MAXE 1000000
 #define inf 1e9-1     
 int ne,nv,tmp,s,t,index; 
 struct Edge{
     int next,pair;
     int v,cap,fLow;
 }edge[MAXE];
    
 int c[5005];

 int net[MAXN];
 int ISAP()
 {
     int numb[MAXN],dist[MAXN],curedge[MAXN],pre[MAXN];
 
     int cur_fLow,max_fLow,u,tmp,neck,i;
     memset(dist,0,sizeof(dist));
     memset(numb,0,sizeof(numb));
     memset(pre,-1,sizeof(pre));
     for(i = 1 ; i <= nv ; ++i)
         curedge[i] = net[i];
     numb[nv] = nv;
     max_fLow = 0;
     u = s;
     while(dist[s] < nv)
     {
         if(u == t)
         {
             cur_fLow = inf;
             for(i = s; i != t;i = edge[curedge[i]].v) 
             {  
                 if(cur_fLow > edge[curedge[i]].cap)
                 {
                     neck = i;
                     cur_fLow = edge[curedge[i]].cap;
                 }
             }
             for(i = s; i != t; i = edge[curedge[i]].v)
             {
                 tmp = curedge[i];
                 edge[tmp].cap -= cur_fLow;
                 edge[tmp].fLow += cur_fLow;
                 tmp = edge[tmp].pair;
                 edge[tmp].cap += cur_fLow;
                 edge[tmp].fLow -= cur_fLow;
             }
             max_fLow += cur_fLow;
             u = neck;
         }
         /* if .... eLse ... */
         for(i = curedge[u]; i != -1; i = edge[i].next)
             if(edge[i].cap > 0 && dist[u] == dist[edge[i].v]+1)
                 break;
         if(i != -1)
         {
             curedge[u] = i;
             pre[edge[i].v] = u;
             u = edge[i].v;
         }else{
             if(0 == --numb[dist[u]]) break;
             curedge[u] = net[u];
             for(tmp = nv,i = net[u]; i != -1; i = edge[i].next)
                 if(edge[i].cap > 0)
                     tmp = tmp<dist[edge[i].v]?tmp:dist[edge[i].v];
             dist[u] = tmp + 1;
             ++numb[dist[u]];
             if(u != s) u = pre[u];
         }
     }     
     return max_fLow;
 }
 void add(int u,int v,int f)
 {
     edge[index].next = net[u];
     edge[index].v = v;
     edge[index].cap = f;
     edge[index].fLow = 0;
     edge[index].pair = index+1;
     net[u] = index++;
     edge[index].next = net[v];
     edge[index].v = u;
     edge[index].cap = 0;
     edge[index].fLow = 0;
     edge[index].pair = index-1;
     net[v] = index++;
    
 }
 int main()
{
 int n,m,a,b,c,x;
 while(~scanf("%d%d",&n,&m))
 {
    index=0;
    memset(net,-1,sizeof(net));
    for(int i = 1  ; i <=  n ; i++)
    {
         scanf("%d",&x);
         add(i,n+m+1,x);      //连汇点
    }
    int sum =0 ;
    for(int i = 1 ; i <= m ; i++)
    {
         scanf("%d%d%d",&a,&b,&c);
         add(0,i+n,c);      //连源点
         add(i+n,a,inf);    //边这个点,连两端点
         add(i+n,b,inf);
         sum += c ;
    }
    s=0;t=n+m+1;nv=t+1;
    printf("%d\n",sum-ISAP());
 }
     return 0;
}

 

//sap模板(建图简单点)

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

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()
 {
     memset(dis,0,sizeof(dis));
     memset(gap,0,sizeof(gap));
      for(int i = 0 ; i < NV ; ++i)
         cur[i] = head[i];
     int u = pre[s] = s,maxflow = 0,aug = inf;
     gap[0] = NV;
     while(dis[s] < NV)
     {
 loop:
         for(int &i = cur[u]; i != -1; i = edge[i].next)
         {
             int v = edge[i].v;
             if(edge[i].c && dis[u] == dis[v] + 1)
             {
                 aug=min(aug,edge[i].c);
                 pre[v] = u;
                 u = v;
                 if(v == t)
                 {
                     maxflow += aug;
                     for(u = pre[u]; v != s; v = u,u = pre[u])
                     {
                         edge[cur[u]].c -= aug;
                         edge[cur[u]^1].c += aug;
                     }
                     aug =inf;
                 }
                 goto loop;
             }
         }
         if( (--gap[dis[u]]) == 0)   break;
         int mindis = NV;
         for(int i = head[u]; i != -1 ; i = edge[i].next)
         {
             int v = edge[i].v;
             if(edge[i].c && mindis > dis[v])
             {
                 cur[u] = i;
                 mindis = dis[v];
             }
         }
         gap[ dis[u] = mindis+1 ] ++;
         u = pre[u];
     }
     return maxflow;
 }


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

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值