HDU 4411 Arrest(费用流)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4411

题意:有n+1个城市,编号0到n。其中警察局在0号城市,1到n号城市中每个城市都有一个小偷。现在警察局里有K个警察。现在让这K个警察去抓小偷(当然抓一个小偷只需要一个警察就行了。。)。

(1)每两个城市之间有距离;

(2)在抓i城市的小偷的时候i-1城市的小偷必须先抓住;

(3)所有警察(也可能用不到K个警察)在抓完小偷后都要返回警察局。

求出所有警察抓小偷所走的路程和

思路:拆点i和i+n(1<=i<=n)。

(1)i+n到j(i<j)连边容量为1,费用为i到j的最短路(抓完i城市的小偷接着去抓j城市的小偷);

(2)0到i,容量为1,费用g[0][i](去抓i城市的小偷);

(3)i+n到汇点,容量为1,费用为g[0][i](回到警察局);

(4)i到i+n,容量为1,费用为负无穷,这样就保证肯定这个城市必被访问到;

(5)源点到0,容量为K,费用为0(有K个警察可用);

(6)在0和汇点连容量为K费用为0的边(有的人可以一直呆在警察局里。。。)

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;

struct node
{
    int u,v,cost,flow,next;
};


const int INF=1000000;
node edges[1000005];
int head[205],e;
int pre[205],in[205],cost[205],flow[205];
int s,t;
queue<int> Q;


int g[105][105],n,m,K;


void add(int u,int v,int flow,int cost)
{
    edges[e].u=u;
    edges[e].v=v;
    edges[e].cost=cost;
    edges[e].flow=flow;
    edges[e].next=head[u];
    head[u]=e++;
}

void Add(int u,int v,int flow,int cost)
{
    add(u,v,flow,cost);
    add(v,u,0,-cost);
}

int SPFA(int s,int t)
{
    int i,u,v;

    memset(pre,-1,sizeof(pre));
    memset(flow,0,sizeof(flow));
    memset(in,0,sizeof(in));
    for(i=0;i<=t;i++) cost[i]=INF;
    while(!Q.empty()) Q.pop();
    Q.push(s);
    in[s]=1;
    flow[s]=INF;
    cost[s]=0;
    while(!Q.empty())
    {
        u=Q.front();
        Q.pop();

        in[u]=0;
        for(i=head[u];i!=-1;i=edges[i].next)
        {
            v=edges[i].v;
            if(edges[i].flow>0&&cost[v]>cost[u]+edges[i].cost)
            {
                cost[v]=cost[u]+edges[i].cost;
                pre[v]=i;
                flow[v]=min(flow[u],edges[i].flow);
                if(!in[v])
                {
                    in[v]=1;
                    Q.push(v);
                }
            }
        }
    }
    return flow[t];
}

int MCMF(int s,int t)
{
    int mincost=0,minflow,i;
    while(minflow=SPFA(s,t))
    {
        for(i=pre[t];i!=-1;i=pre[edges[i].u])
        {
            mincost+=minflow*edges[i].cost;
            edges[i].flow-=minflow;
            edges[i^1].flow+=minflow;
        }
    }
    return mincost;
}

void init()
{
    memset(head,-1,sizeof(head));
    e=0;
    s=n+n+1;
    t=s+1;
    int i,j;
    Add(s,0,K,0);
    Add(0,t,K,0);
    for(i=1;i<=n;i++)
    {
        Add(i,i+n,1,-INF);
        if(g[0][i]<INF)
        {
            Add(0,i,INF,g[0][i]);
            Add(i+n,t,INF,g[i][0]);
        }
        for(j=i+1;j<=n;j++) if(g[i][j]<INF)
            Add(i+n,j,1,g[i][j]);
    }
}



int main()
{
    while(scanf("%d%d%d",&n,&m,&K),n||m||K)
    {
        int i,j,k,u,v,D;
        for(i=0;i<=n;i++) for(j=0;j<=n;j++) g[i][j]=INF;
        for(i=0;i<=n;i++) g[i][i]=0;
        while(m--)
        {
            scanf("%d%d%d",&u,&v,&D);
            g[u][v]=g[v][u]=min(g[u][v],D);
        }
        for(k=0;k<=n;k++) for(i=0;i<=n;i++) for(j=0;j<=n;j++)
            g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
        init();
        printf("%d\n",MCMF(s,t)+n*INF);
    }
    return 0;
}

  

 

 

转载于:https://www.cnblogs.com/jianglangcaijin/archive/2012/09/24/2700509.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值