D - Farm Tour(最小费用流)

D - Farm Tour(最小费用流)

POJ - 2135

思路:

​ 问题可以转化为求两条 1 1 1 n n n的路径,使得这两条路径没有重边且费用和最小。而这个问题我们又可以转化为最小费用流问题。对于 u u u v v v的费用为 w w w双向边,在图中转化为 u u u v v v容量 1 1 1费用为 w w w的边和 v v v u u u容量为 1 1 1,费用为 w w w的边; 求 1 1 1 n n n流量为 2 ​ 2​ 2的最小费用即可。

由网络流的意义可知,残留网络图中不会使双向边同时存在。

等效性证明:
对于在问题中的每一中方案都对应网络图中一个费用流,且每一个费用流都对应图中的一个方案。故这两个问题等效,所以我们求最小花费即求最小费用流。

代码:

#include
#include
#include
#include
#include
#include
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn=1100;//顶点数量
const int inf=0x3f3f3f3f;
struct edge
{
    int to,cap,cost,rev;
    edge() {}
    edge(int to,int cap,int cost,int rev)
    {
        this->to=to,this->cap=cap,this->cost=cost,this->rev=rev;
    }
};
class MCMF
{
public:
    vector<edge> adja[maxn];
    int dis[maxn],prevv[maxn],preve[maxn],top;
    bool inque[maxn];
    void init(int n)
    {
        for(int i=0; i<=n; ++i)    adja[i].clear();
        top=n;
    }
    void addEdge(int u,int v,int f,int cost)
    {
        adja[u].push_back(edge(v,f,cost,adja[v].size()));
        adja[v].push_back(edge(u,0,-1*cost,adja[u].size()-1));
    }
    bool spfa(int s,int t)
    {
        queue<int> mp;
        mset(dis,inf);
        mset(prevv,-1);
        mset(inque,0);
        mp.push(s),prevv[s]=s,dis[s]=0,inque[s]=true;
        while(!mp.empty())
        {
            int u=mp.front();
            mp.pop();
            inque[u]=false;
            for(int i=0; i<adja[u].size(); ++i)
            {
                edge& e=adja[u][i];
                if(e.cap>0&&dis[e.to]>dis[u]+e.cost)
                {
                    dis[e.to]=dis[u]+e.cost;
                    prevv[e.to]=u;
                    preve[e.to]=i;
                    if(!inque[e.to])
                    {
                        inque[e.to]=true;
                        mp.push(e.to);
                    }
                }
            }
        }
        if(~prevv[t]) return true;
        return false;
    }
    //他的第三个函数也可以这样写,即满足流f的最小花费
    int minCostMaxFlow(int s,int t,int f) //不能满足流f则返回-1
    {
        int cost=0;
        while(f>0)
        {
            spfa(s,t);
            if(dis[t]==inf)
                return -1;
            int d=f;
            for(int v=t; v!=prevv[v]; v=prevv[v]) //找到d
                d=min(d,adja[prevv[v]][preve[v]].cap);
            cost+=d*dis[t];
            f-=d;
            for(int v=t; v!=prevv[v]; v=prevv[v])
            {
                edge &e=adja[prevv[v]][preve[v]];
                e.cap-=d;
                adja[v][e.rev].cap+=d;
            }
        }
        return cost;
    }

};

MCMF kit;
int main()
{
    /*s:1,t:n 按给出的添加边,cap=1,容量为-1*/
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        kit.init(n);
        for(int i=1; i<=m; ++i)
        {
            int u,v,cost;
            scanf("%d %d %d",&u,&v,&cost);
            kit.addEdge(u,v,1,cost);
            kit.addEdge(v,u,1,cost);
        }
        int ans=kit.minCostMaxFlow(1,n,2);
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(#,最小费用流)