hdu 3879 Base Station (最大权闭合图)
2013-10-21 20:31
369 查看
结论:正的权值的和-建图后的最小割的容量
选择了一条边就会选择两个点,边的花费为正,点的花费为负,把边看成点,这个点向两个端点连一条边,表示选择这条边就会选择这两个点
然后题目就相当于最大权闭合图的模型了
题意:有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
,head
,gap
,dis
,cur
;
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;
}
选择了一条边就会选择两个点,边的花费为正,点的花费为负,把边看成点,这个点向两个端点连一条边,表示选择这条边就会选择这两个点
然后题目就相当于最大权闭合图的模型了
题意:有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
,head
,gap
,dis
,cur
;
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;
}
相关文章推荐
- Android 代码片段---从相册或相机获取图片保存并处理
- VC++6.0 修改应用程序的图标
- 第三方支付业务浅析
- windows7 安装VS2005和wince6.0
- 选择排序
- IOS的变量前加extern和static字段
- ASP.NET弹出新窗口的方法【显示提示信息】
- strstr函数的简单实现
- break,continue,goto,Return几个方法
- iOS 文件和数据管理 (可能会删除本地文件储存)
- The literal 100000000000000 of type int is out of range
- xcode 4.6.3 用cocos2d-iphone+box2d模拟器设置竖屏问题
- for循环执行效率
- for循环执行效率
- mysql导入及导出txt文件
- 冒泡排序
- Linux驱动编程--基于I2C子系统的I2C驱动的Makefile
- usaco 购买饲料 && 修剪草坪
- STL中array<>内部为什么不能交换指针呢
- C++函数模板