您的位置:首页 > 理论基础 > 计算机网络

bzoj1497: [NOI2006]最大获利(网络流)

2017-10-25 16:21 405 查看
题目传送门

真的皮。

解法:

这道题不会啊。

%了题解都说是最大权闭合子图。

最大权闭合子图的方法。

st向正权点(可以赚钱的)连容量为利益的边。

负权点(要花钱的)向ed连容量为花的钱的边。

图中的边(对应的利益和花费)连容量为无限大的边。

答案为总利益-最大流

为什么?

花了好久来想:

如果一条路径上面流过的流量是等于他的花费的。

说明他的利益是大于花费的,那么赚钱肯定要选。

所以总利益-最大流肯定包括了这个利益。

如果一条路径上面流过的流量是等于他的利益的。

也就是说他的利益小于花费的,那么肯定不选啦。

所以总利益-最大流就表示了不选这个东西。

强啊。

代码实现:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
struct node {
int x,y,c,next,other;
}a[1100000];int len,last[110000];
void ins(int x,int y,int c) {
int k1,k2;
len++;k1=len;
a[len].x=x;a[len].y=y;a[len].c=c;
a[len].next=last[x];last[x]=len;
len++;k2=len;
a[len].x=y;a[len].y=x;a[len].c=0;
a[len].next=last[y];last[y]=len;
a[k1].other=k2;a[k2].other=k1;
}
int head,tail,list[110000],h[110000],st,ed;
bool bfs() {
memset(h,0,sizeof(h));h[st]=1;
head=1;tail=2;list[1]=st;
while(head!=tail) {
int x=list[head];
for(int k=last[x];k;k=a[k].next) {
int y=a[k].y;
if(h[y]==0&&a[k].c>0) {
h[y]=h[x]+1;
list[tail++]=y;
}
}
head++;
}
if(h[ed]==0)
return false;
return true;
}
int findflow(int x,int f){
if(x==ed)
return f;
int s=0,t;
for(int k=last[x];k;k=a[k].next) {
int y=a[k].y;
if(h[y]==h[x]+1&&a[k].c>0&&s<f) {
t=findflow(y,min(a[k].c,f-s));
s+=t;a[k].c-=t;a[a[k].other].c+=t;
}
}
if(s==0)
h[x]=0;
return s;
}
int main() {
int n,m;scanf("%d%d",&n,&m);
st=n+m+1;ed=st+1;
for(int i=1;i<=n;i++) {
int x;scanf("%d",&x);
ins(i,ed,x);   //负权点向ed连边容量为花费。
}
int sum=0;
for(int i=1;i<=m;i++) {
int x,y,c;scanf("%d%d%d",&x,&y,&c);
ins(i+n,x,999999999);  //原图对应边
ins(i+n,y,999999999);  //原图对应边
ins(st,i+n,c);  //st向正权点连边容量为利益
sum+=c;
}
int ans=0;
while(bfs()==true)
ans+=findflow(st,999999999);
printf("%d\n",sum-ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: