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

HDU 4411 Arrest 费用流

2016-04-17 17:02 459 查看
费用流是每次找最短路然后沿着这条路增广,然后这条路因为增广了,所以就流量为0,相当于不存在了,所以可以继续找最短路,不怕出现重复,然后求的是全图的,这点切记

有n+1个城市,0市为警察局所在城市其中有k个警察,在其他n个城市中,均有小偷。有m条路,每条路有其特定路程,求k个警察将n个城市的小偷全抓捕到0市的最短路程。其中抓小偷必须从1、2、3、、、n按照城市号码严格递增顺序抓。

最小费用流的问题。样例输出解释:0->1->3(不抓小偷)->2->3->1->0。路程和为3+2+2+2+2+3=14。

设置超级源点s,超级汇点t。将0点与s和t建立容量为k,费用为0的边,并将其他n点拆掉。n个点与0点建立容量1费用为两点最短路的边,同时n个被拆出来的点与t建立容量为1费用为两点最短路的边,n个点与其被拆出来的点建立容量为1费用足够小的边(足够小的意思是保证不会出现s->0->t),跑一边最小费用,最后输出的时候把n个足够小费用加回去。

#include <iostream>
#include <stdio.h>
using namespace std;
const int oo=1e9;
const int maxm=1111111;
const int maxn=220;
const int ff=100000;

int node,src,dest,edge;
int head[maxn],p[maxn],dis[maxn],q[maxn],vis[maxn];

struct edgenode
{
int to;
int flow;
int cost;
int next;
} edges[maxm];

void prepare(int _node,int _src,int _dest);
void addedge(int u,int v,int f,int c);
bool spfa();

inline int min(int a,int b)
{
return a<b?a:b;
}

inline void prepare(int _node,int _src,int _dest)
{
node=_node;
src=_src;
dest=_dest;
for (int i=0; i<node; i++)
{
head[i]=-1;
vis[i]=false;
}
edge=0;
}

void addedge(int u,int v,int f,int c)
{
edges[edge].flow=f;
edges[edge].cost=c;
edges[edge].to=v;
edges[edge].next=head[u];
head[u]=edge++;
edges[edge].flow=0;
edges[edge].cost=-c;
edges[edge].to=u;
edges[edge].next=head[v];
head[v]=edge++;
}

bool spfa()
{
int i,u,v,l,r=0,tmp;
for (i=0; i<node; i++) dis[i]=oo;
dis[q[r++]=src]=0;
p[src]=p[dest]=-1;
for (l=0; l!=r; ((++l>=maxn)?l=0:1))
{
for (i=head[u=q[l]],vis[u]=false; i!=-1; i=edges[i].next)
{
if (edges[i].flow&&dis[v=edges[i].to]>(tmp=dis[u]+edges[i].cost))
{
dis[v]=tmp;
p[v]=i^1;
if (vis[v]) continue;
vis[q[r++]=v]=true;
if (r>=maxn) r=0;
}
}
}
return p[dest]>=0;
}

int spfaflow()
{
int i,ret=0,delta;
while (spfa())
{
for (i=p[dest],delta=oo; i>=0; i=p[edges[i].to])
{
delta=min(delta,edges[i^1].flow);
}
for (int i=p[dest]; i>=0; i=p[edges[i].to])
{
edges[i].flow+=delta;
edges[i^1].flow-=delta;
}
ret+=delta*dis[dest];
}
return ret;
}//以上是费用流模板

int a[maxn][maxn];
int main()
{
int n,m,k;
while(~scanf("%d%d%d",&n,&m,&k) && (n || m || k))
{
//init();
int  s=n*2+1;
int  t=s+1;
prepare(n*2+3,s,t);
//for(int i=0; i<=n; i++)
// for(int j=0; j<=n; j++)
for(int i=0; i<m; i++)
for(int j=0; j<m; j++)
if (i!=j) a[i][j]=oo;
else a[i][j]=0;
//while(m--)
for(int i=0; i<m; i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
a[u][v]=a[v][u]=min(w,a[u][v]);
}
for(int l=0;l<=n;l++)//Floyd求最短路
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
if(a[i][l]<oo && a[l][j]<oo && a[i][l]+a[l][j]<a[i][j])
a[i][j]=a[i][l]+a[l][j];
addedge(s,0,k,0);//源点与0点,容量为k,费用为0
addedge(0,t,k,0);//汇点与0点,容量为k,费用为0
for(int i=1; i<=n; i++)
{
for(int j=i+1; j<=n; j++)
{
addedge(i+n,j,1,a[i][j]);//先到i点再到j点
}
addedge(0,i,1,a[0][i]);//0点到i点,容量为1,费用为最短路
addedge(i,i+n,1,-ff);//拆点,cost值设为适当小的值,以免导致s->0->t
addedge(i+n,t,1,a[0][i]);//i点到0点,容量为1,费用为最短路
}
printf("%d\n",spfaflow()+ff*n);

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