您的位置:首页 > 其它

BZOJ 1497: [NOI2006]最大获利

2017-10-26 15:37 375 查看
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1497

/**
转化成最大权闭合子图,如果要满足用户群C,就要建立A,B站点
把用户群看成一个个节点,则可以把他们划分成两个集合,第一个
集合是得到满足的集合,另一个是未得到满足的集合,满足的设为
S集合,未满足设未T集合,对于一个用户群,如果它属于T集合,则
会损失一些收益,对于每一个点,如果有收益,其点权值未正值,
如果需要建站,则点权值是负值,按照胡伯涛论文中所述,对于点
权是正值的点,我们与超级源点S建立关系,其值就是点的权值,如果
点权是赋值的点,与超级汇点建立联系,其权值是点权的绝对值,
然后点与点之间有联系的话,建立边权值未INF。
对于这个题目,从源点S与每一个用户群建立一条边,权值未满足该
用户群后所能获得的收益,每一个基站与超级汇点建立一条边,其
权值为建立该基站的费用,又由于满足该用户群,需要建立A,B基站,
则添加该用户群节点到A的边其权值为INF,添加该用户群节点到B的
边其权值为INF。纯收入 = 总利润-成本=(所有正的点权值之和)-最小割
最终求得最小割,从S出发进行dfs,所能遍历到得点都是属于S的点,
用户群属于S则,则满足该用户群的基站也必然属于S割,因为用户
群到这两个点的边都是无穷大,没有流可以阻断权重为无穷大的边。
只要用户群属于S割,则可以获取其收益。所以我们就先假设能获取
所有的收益最后减去用户群属于T割造成的损失,就是纯利润。
*/

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>

using namespace std;

const int maxn = 60000;
const int INF = 0x7fffffff;
struct Edge {
int to,cap,nex;
}edge[maxn<<3];
int cnt,level[maxn],head[maxn],cur[maxn],S,T,N,M;
void addEdge(int u,int v,int w) {
edge[cnt].to = v;
edge[cnt].cap = w;
edge[cnt].nex = head[u];
head[u] = cnt++;

edge[cnt].to = u;
edge[cnt].cap = 0;
edge[cnt].nex = head[v];
head[v] = cnt++;
}
bool bfs() {
memset(level,-1,sizeof(level));
queue<int>qu;
level[S] = 0;
qu.push(S);
while(!qu.empty()) {
int u = qu.front();
qu.pop();
for(int i = head[u]; i != -1; i = edge[i].nex) {
int v = edge[i].to;
if(edge[i].cap>0 && level[v]==-1) {
level[v] = level[u] + 1;
qu.push(v);
}
}
}
if(level[T] == -1)
return false;
else
return true;
}
int dfs(int u,int flow) {
if(u == T) return flow;
int tmp,sum = 0;
for(int &i = cur[u]; i != -1; i = edge[i].nex) {
int v = edge[i].to;
if(edge[i].cap>0 && level[v]==level[u]+1) {
tmp = dfs(v,min(flow-sum,edge[i].cap));
edge[i].cap -= tmp;
edge[i^1].cap += tmp;
sum += tmp; ///sum是流出的流量
if(sum == flow) return flow; ///节点流量平衡,返回
}
}
if(sum == 0) level[u] = -1;
return sum;
}
int dinic() {
int maxFlow = 0;
while(bfs()) {
for(int i = S; i <= T; i++)
cur[i] = head[i];
maxFlow += dfs(S,INF);
}
return maxFlow;
}
int main() {
while(~scanf("%d%d",&N,&M)) {
int cost;
cnt = 0;
memset(head,-1,sizeof(head));
S = 0;
T = N+M+1;
for(int i = 1; i <= N; i++) {
scanf("%d",&cost);
addEdge(i,T,cost);
}
int profit = 0;
int A,B,C;
for(int i = 1; i <= M; i++) {
scanf("%d%d%d",&A,&B,&C);
addEdge(S,N+i,C);
addEdge(N+i,A,INF);
addEdge(N+i,B,INF);
profit += C;
}
int ans = profit - dinic();
printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: