hdu 4411 网络赛题目 费用流

/*
题意:有n(0<=n<=100)个点m(0<=m<=4000)条边的无向图,
有k(0<=k<=25)个人从0点出发,依次占领1~n点,因为 i 被占领的时候会通知
i - 1 点,到达一个点的时候可以选择不占领,问最后<=k个人占领所有地方再走回来的最小值。
思路,分析:
 最小费用最大流,需要注意的地方在于怎么去保证每个每个城市的团伙仅仅被抓一次,且在抓他之前,第i-1城市的团伙已经被抓。

          方法是把拆点后的城市 i 和 i`之间的费用要设成一个很小的负值,这样可以保证该城市一定可以被访问到,

          还有一点要注意的是派出的k个队可能有些队是不执行任务的,

          方法是在 0 节点和 汇点之间连一条费用为0,容量为k的边

          具体建图:

          源点 s=2*n+1,

          汇点 t=2*n+2,

          每个城市拆成两个点 i 和 i+n,费用为 -100000,容量为 1

          在源点和 0 之间连一条费用为 0 容量为 k 的边

          在 0 和 城市 1..n之间连一条费用为 0 到 i 最短路容量为 1 的边(表示出发)

          在 城市 n+1..n+n到汇点之间连一条费用为 0 到 i 最短路容量为 1 的边(表示回到总部)

          在 城市 n+i..n+n 和 j(j>i)直间连一条费用为城市 i 到 j 最短路距离容量为 1 的边

          求最小费用流。

*/

/*
题意:有n(0<=n<=100)个点m(0<=m<=4000)条边的无向图,
有k(0<=k<=25)个人从0点出发,依次占领1~n点,因为 i 被占领的时候会通知
i - 1 点,到达一个点的时候可以选择不占领,问最后<=k个人占领所有地方再走回来的最小值。
思路,分析:
 最小费用最大流,需要注意的地方在于怎么去保证每个每个城市的团伙仅仅被抓一次,且在抓他之前,第i-1城市的团伙已经被抓。

          方法是把拆点后的城市 i 和 i`之间的费用要设成一个很小的负值,这样可以保证该城市一定可以被访问到,

          还有一点要注意的是派出的k个队可能有些队是不执行任务的,

          方法是在 0 节点和 汇点之间连一条费用为0,容量为k的边

          具体建图:

          源点 s=2*n+1,

          汇点 t=2*n+2,

          每个城市拆成两个点 i 和 i+n,费用为 -100000,容量为 1

          在源点和 0 之间连一条费用为 0 容量为 k 的边

          在 0 和 城市 1..n之间连一条费用为 0 到 i 最短路容量为 1 的边(表示出发)

          在 城市 n+1..n+n到汇点之间连一条费用为 0 到 i 最短路容量为 1 的边(表示回到总部)

          在 城市 n+i..n+n 和 j(j>i)直间连一条费用为城市 i 到 j 最短路距离容量为 1 的边

          求最小费用流。
*/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cstdio>
using namespace std;
const int M=2202;
const int MAXN=400002;
const int INF=100000;
const int inf=1<<29;
int pre[M];          // pre[v] = k:在增广路上,到达点v的边的编号为k
int dis[M];          // dis[u] = d:从起点s到点u的路径长为d
int vis[M];         // inq[u]:点u是否在队列中
int path[M];
int head[M];
int n,m,NE,sink,tot,ans,max_flow,map[M][M];
struct node
{
    int u,v,cap,cost,next;
} Edge[MAXN<<2];
void addEdge(int u,int v,int cap,int cost)
{
    Edge[NE].u=u;
    Edge[NE].v=v;
    Edge[NE].cap=cap;
    Edge[NE].cost=cost;
    Edge[NE].next=head[u];
    head[u]=NE++;
    Edge[NE].v=u;
    Edge[NE].u=v;
    Edge[NE].cap=0;
    Edge[NE].cost=-cost;
    Edge[NE].next=head[v];
    head[v]=NE++;
}
void floyd(int n)
{
    int i,j,k;
    for(k=0;k<=n;k++)
    {
        for(i=0;i<=n;i++)
        {
            for(j=0;j<=n;j++)
            {
                if(map[i][j]>map[i][k]+map[k][j])
                {
                    map[i][j]=map[i][k]+map[k][j];
                }
            }
        }
    }
}
bool SPFA(int s,int t)                   //  源点为s,汇点为sink。
{
    int i;
    for(i=0; i<=n+1; i++) dis[i]=inf;
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    dis[s] = 0;
    queue<int>q;
    q.push(s);
    vis[s] =1;
    while(!q.empty())        //  这里最好用队列,有广搜的意思,堆栈像深搜。
    {
        int u =q.front();
        q.pop();
        for(i = head[u]; i != -1; i = Edge[i].next)
        {
            int v = Edge[i].v;
            if(Edge[i].cap >0&& dis[v] > dis[u] + Edge[i].cost)
            {
                dis[v] = dis[u] + Edge[i].cost;
                pre[v] = u;
                path[v]=i;
                if(!vis[v])
                {
                    vis[v] =1;
                    q.push(v);
                }
            }
        }
        vis[u] =0;
    }
    if(pre[t]==-1)
        return false;
    return true;
}
void end(int s,int t)
{
    int u, p,sum = inf;
    for(u=t; u!=s; u=pre[u])
    {
        sum = min(sum,Edge[path[u]].cap);
    }
    max_flow+=sum;
    for(u = t; u != s; u=pre[u])
    {
        Edge[path[u]].cap -= sum;
        Edge[path[u]^1].cap += sum;
        ans += sum*Edge[path[u]].cost;     //  cost记录的为单位流量费用,必须得乘以流量。
    }
}
int main()
{
    int i,j,k,s,t,u,v,w,tmp;
    //freopen("//media/学习/ACM/input.txt","r",stdin);
    while(scanf("%d%d%d",&n,&m,&k),(n||m||k))
    {
        memset(head,-1,sizeof(head));
        NE=ans=max_flow=0;
        s=2*n+1;
        t=2*n+2;
        for(i=0;i<=n;i++)
        {
            for(j=0;j<=n;j++)
            {
                map[i][j]=inf;
            }
            map[i][i]=0;
        }
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            map[u][v]=map[v][u]=min(map[u][v],w);
        }
        floyd(n);
        addEdge(s,0,k,0);
        addEdge(0,t,k,0);
        for(i=1;i<=n;i++)
        {
            addEdge(0,i,1,map[0][i]);
            addEdge(i,i+n,1,-INF);
            addEdge(i+n,t,1,map[0][i]);
        }
        for(i=1;i<=n;i++)
        {
            for(j=i+1;j<=n;j++)
            {
                addEdge(i+n,j,1,map[i][j]);
            }
        }
        tmp=n;
        n=t+1;
        while(SPFA(s,t))
        {
            end(s,t);
        }
        printf("%d\n",ans+INF*tmp);
    }
    return 0;
}


你可能感兴趣的:(hdu 4411 网络赛题目 费用流)