【最大权闭合子图】BZOJ1497[NOI2006]-最大获利

【题目大意】

建立第i个通讯中转站需要的成本为Pi(1≤i≤N)。另外公司调查得出了所有期望中的用户群,一共M个。关于第i个用户群的信息概括为Ai, Bi和Ci:这些用户会使用中转站Ai和中转站Bi进行通讯,公司可以获益Ci。(1≤i≤M, 1≤Ai, Bi≤N) THU集团的CS&T公司可以有选择的建立一些中转站(投入成本),为一些用户提供服务并获得收益(获益之和)。那么如何选择最终建立的中转站才能让公司的净获利最大呢?(净获利 = 获益之和 - 投入成本之和)

【思路】

根据最大权闭合子图的结论进行建图,超级源点和中转站相连,容量为成本;A与B中转站分别连向某个用户,容量为INF;再由某个用户连向超级汇点,容量为收益值。

最终答案=∑(所有用户群的总收益)-最大流。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstdlib>
  4 #include<cstring>
  5 #include<algorithm>
  6 #include<cstdlib>
  7 #include<vector>
  8 #include<cmath>
  9 #include<queue>
 10 #define target m+n+1 
 11 using namespace std;
 12 const int MAXN=600000;
 13 const int INF=0x7fffffff;
 14 struct node 
 15 {
 16     int to,pos,cap;
 17 };
 18 vector<node> E[MAXN];
 19 int n,m;
 20 int sum=0;
22 int dist[MAXN];
 23 
 24 void addedge(int u,int v,int w) 
 25 {
 26     E[u].push_back((node){v,E[v].size(),w});
 27     E[v].push_back((node){u,E[u].size()-1,0});
 28 }
 29 
 30 void init()
 31 {    
 32     scanf("%d%d",&n,&m);
 33     for (int i=1;i<=n;i++)
 34     {
 35         int p;
 36         scanf("%d",&p);
 37         addedge(0,i,p);
 38     }
 39     for (int i=1;i<=m;i++)
 40     {
 41         int a,b,c;
 42         scanf("%d%d%d",&a,&b,&c);
 43         addedge(a,i+n,INF);
 44         addedge(b,i+n,INF);
 45         addedge(i+n,target,c);
 46         sum+=c;
 47     }
 48 }
 49 
 50 int bfs() 
 51 {
 52     memset(dist,-1,sizeof(dist));
 53     queue<int> que;
 54     while (!que.empty()) que.pop();
 55     que.push(0);
 56     dist[0]=0;
 57     
 58     while (!que.empty()) 
 59     {
 60         int head=que.front();
 61         que.pop();
 62         for (int i=0; i<E[head].size(); i++) 
 63         {
 64             node &tmp=E[head][i];
 65             if (dist[tmp.to]==-1 && tmp.cap>0)
 66             {    
 67                 dist[tmp.to]=dist[head]+1;
 68                 que.push(tmp.to);
 69                 if (tmp.to==target) return 1;
 70             }
 71         }
 72     }
 73     return 0;
 74 }
 75 
 76 
 77 int dfs(int s,int e,int f) 
 78 {
 79     int ret=0;
 80     if (s==e || f==0) return(f);
 81     for (int i=0; i<E[s].size(); i++)//此处可添加当前弧优化,但是效率反而会低orz 
 82     {
 83         node  &tmp=E[s][i];
 84         if (dist[tmp.to]==dist[s]+1 && tmp.cap>0) 
 85         {
 86             int delta=dfs(tmp.to,e,min(tmp.cap,f));
 87             if (delta>0) 
 88             {
 89                 ret+=delta;
 90                 tmp.cap-=delta;
 91                 E[tmp.to][tmp.pos].cap+=delta;
 92                 f-=delta;//不要忘记f要减去delta! 没有加的时候是60s,加了ret累加后瞬间快了 
 93             }
 94         }
 95     }
 96     return ret;
 97 }
 98 
 99 void dinic()
100 {
101     int flow=0;
102     while (bfs()) 
103     {
104         for (;;) 
105         {
106             int f=dfs(0,target,INF);
108             if (f==0) break;
109             else flow+=f;
110         }
111     }
112     int ans=sum-flow;
113     cout<<ans<<endl;
114 }
115 
116 int main()
117 {
118     init();
119     dinic();
120     return 0;
121 }

 

转载于:https://www.cnblogs.com/iiyiyi/p/5246582.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值