您的位置:首页 > 其它

HDU 4289 Control (最大流+拆点)

2014-08-13 11:33 344 查看
http://acm.hdu.edu.cn/showproblem.php?pid=4289

题目讲的是有一些恐怖分子要从S市去往D市,要求在一些城市中安排特工,保证一定能够抓住恐怖分子,因为安排特工需要一定的费用,所以希望找出最小的花费。

思路:可以把每个城市,即每个点拆分成进来的点和出去的点,如x点分成x和x+n,两点连接的边权值为x点上安排特工的费用。而如果x和y两点有连线,则连接x+n,y,然后求从S市到达D市的最大流。之所以能这样求,是因为在求最大流的过程中每次所更新的流量是所有边中最小的,这样最小流量的边就是每个点拆分开的两点之间的连线,这样求的过程中对最大流有限制的就是所有点上的花费了。

/*Dinic算法求最大流*/
#include<stdio.h>
#include<string.h>
#include<iostream>
#define point_MAX 10000
#define edge_MAX 100000
#define INF_MAX 999999999
using namespace std;
struct EDGE
{
int to;/*指向的点*/
int next;/*指向的下一条邻边*/
int w;/*权值*/
}edge[edge_MAX];
int len;/*边的数量*/
int point[point_MAX];
int Vertex,Edge;
int d[point_MAX];
void init()/*初始化*/
{
len=0;
memset(point,0,sizeof(point));
}
int add_edge(int a,int b,int w)/*添加由a指向b的权值为w的边*/
{
len++;
edge[len].w=w;
edge[len].to=b;
edge[len].next=point[a];
point[a]=len;
return 0;/*无重边,插入*/
}
int bfs(int s)
{
int q[point_MAX],front=0,rear=1,j,t,i;
q[0]=s;
memset(d,-1,sizeof(d));/**/
d[s]=0;
while(front<rear)
{
t=q[front++];
for(j=point[t];j;j=edge[j].next)
{
if(d[edge[j].to]==-1&&edge[j].w>0)
{
d[edge[j].to]=d[t]+1;
q[rear++]=edge[j].to;/*逐层增加*/
}
}
}
if(d[Vertex]>=0)
return 1;
return 0;
}
long long min(long long a,long long b)
{
return a<b?a:b;
}
long long dinic(int t,long long sum)/*寻找增广路*/
{
int i,os,j;
long long a;
if(t==Vertex)/*如果已经找到汇点,返回sum*/
return sum;
os=sum;
for(i=point[t];i&∑i=edge[i].next)
{
if(d[edge[i].to]==d[t]+1&&edge[i].w>0)/*可行流,即增广路*/
{
a=dinic(edge[i].to,min(sum,edge[i].w));
edge[i].w-=a;
for(j=point[edge[i].to];edge[j].to!=t;j=edge[j].next);
edge[j].w+=a;/*处理反向边*/
sum-=a;
}
}
return os-sum;
}
long long DINIC(int s)/*DINIC算法*/
{
long long ans=0;
while(bfs(s))/*遍历整个图,判断是否已经完成最大流*/
ans+=dinic(s,INF_MAX);/*添加所能增加的流量*/
return ans;
}

int main()
{
int i,j,x,y,w,S,D;
int cost[205];
while(scanf("%d%d",&Vertex,&Edge)!=EOF)
{
init();
memset(cost,0,sizeof(cost));
scanf("%d%d",&S,&D);
for(i=1;i<=Vertex;i++)
{
scanf("%d",&cost[i]);
add_edge(i,i+Vertex,cost[i]);
add_edge(i+Vertex,i,0);
}
for(i=0;i<Edge;i++)
{
scanf("%d%d",&x,&y);
add_edge(x+Vertex,y,INF_MAX);
add_edge(y,x+Vertex,0);
add_edge(y+Vertex,x,INF_MAX);/*添加反向边*/
add_edge(x,y+Vertex,0);
}
Vertex=D+Vertex;
//cout<<"=="<<endl;
printf("%lld\n",DINIC(S));/*以S为源点,Vertex为汇点*/
}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: