bzoj 1877 晨跑(费用流)

求的是最多的天数,和这些天跑步总路程最小.

我们将每两个点之间看成流量为一的通道,那么从源点到汇点,最大的流量就是从寝室跑到学校最多的路线,即最多天数.

并且题目里要求每个点只经过一次,那么需要将每一个点拆分成两个点,之间的边流量设置成一,这样就会保证只有一条路通过这个点了.剩下的就是套模板了...

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<string.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int maxn=500;
int dis[maxn];
int vis[maxn];
int pre[maxn];
int fflow,ccost;
struct Lu
{
    int to;
    int flow;
    int cost;
    Lu(int to=0,int flow=0,int cost=0):to(to),flow(flow),cost(cost) {}
};
vector<Lu>E;
vector<int>V[maxn];
int spfa(int s,int t)
{

    for(int i=s; i<=t; i++)
    {
        pre[i]=-1;
        vis[i]=0;
        dis[i]=inf;
    }
    queue<int>Q;
    dis[s]=0;
    vis[s]=1;
    Q.push(s);
    int top,vlen;
    Lu L;
    while(!Q.empty())
    {
        top=Q.front();
        Q.pop();
        vis[top]=0;
        vlen=V[top].size();
        for(int i=0; i<vlen; i++)
        {
            L=E[V[top][i]];
            if(L.flow>0&&dis[L.to]>dis[top]+L.cost)
            {

                dis[L.to]=dis[top]+L.cost;
                pre[L.to]=V[top][i];//记得是边标号,不是前驱点
                if(!vis[L.to])
                {
                    Q.push(L.to);
                    vis[L.to]=1;
                }
            }

        }
    }
    if(dis[t]==inf)return 0;
    return 1;
}
void MinCostMaxFlow(int s,int t)//模板
{
    fflow=0;
    ccost=0;
    int minl=inf;
    while(spfa(s,t))
    {

        minl=inf;
        for(int i=pre[t]; i!=-1; i=pre[E[i^1].to])
        {
            minl=min(minl,E[i].flow);

        }
        fflow+=minl;
        for(int i=pre[t]; i!=-1; i=pre[E[i^1].to])
        {
            E[i].flow-=minl;
            E[i^1].flow+=minl;
            ccost+=minl*E[i].cost;
            //cout<<ccost<<endl;
        }
    }
    return ;
}
int main()
{
    int n,m,a,b,c;
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0; i<=2*n+2; i++)V[i].clear();
        E.clear();
        E.push_back(Lu(1+n,inf,0));//源点可以经过很多次,那么拆分成之后相连的边容量设成无限大
        E.push_back(Lu(1,0,0));//反向弧
        V[1+n].push_back(E.size()-1);
        V[1].push_back(E.size()-2);
        for(int i=2; i<=n; i++)
        {
            E.push_back(Lu(i+n,1,0));
            E.push_back(Lu(i,0,0));
            V[i+n].push_back(E.size()-1);
            V[i].push_back(E.size()-2);
        }
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            E.push_back(Lu(b,1,c));
            E.push_back(Lu(n+a,0,0-c));
            V[b].push_back(E.size()-1);
            V[n+a].push_back(E.size()-2);
        }
        E.push_back(Lu(n+n,inf,0));////汇点可以经过很多次,那么拆分成之后相连的边容量设成无限大
        E.push_back(Lu(n,0,0));
        V[n+n].push_back(E.size()-1);
        V[n].push_back(E.size()-2);
        MinCostMaxFlow(1,n+n);
        printf("%d %d\n",fflow,ccost);
    }
}

猜你喜欢

转载自blog.csdn.net/zyy_1998/article/details/81432724
今日推荐