POJ 2135 Farm Tour(最小费用流)

若不是看了题目分类,很难想到这题和网络流挂上钩,乍一看是求2遍最短路,但是很明显求最短路可能是错误的。

题意:FJ(又是他><)想带朋友参观自己的农场,农场有n个点,m条边,要从1点到n点,再从n点走回来,且2次不能走相同的路,问最小的花费时间。

题解:费用流,最小费用即为答案

我的构图方法:定义源点和汇点,

1.所有边权值为1,费用为所花费的时间。

2.将源点与1连接边权值为2,费用0,n与汇点相连,权值为2,费用为0.

这样保证了从1到n恰好有2条边不重复的可行流,构成了一个由1到n再到1的回路。

 

#include <cstdio>
#include <cstring>
#define min(a,b) (a>b?b:a)

const int maxn=1005;
const int maxm=60050;
const int inf=0x4fffffff;
struct Edge{
    int v,next,c,w;
}edge[maxm];
int head[maxn],cnt;

void addedge(int u,int v,int w,int c)
{
    edge[cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].c=c;
    edge[cnt].next=head[u];
    head[u]=cnt++;

    edge[cnt].v=u;
    edge[cnt].w=0;
    edge[cnt].c=-c;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}

int dis[maxn],pre[maxn];//最小费用和前驱结点
int alpha[maxn];//标记
int que[maxn],qhead,qrear;

int spfa (int s,int e)//源汇点
{
    //寻找费用增广,没有返回-1;
    for (int i=0 ; i<maxn ; ++i)
        dis[i]=inf;
    memset (alpha , 0 , sizeof(alpha));
    dis[s]=0;
    que[qhead=0]=s;
    qrear=1;
    alpha[s]=1;
    while (qhead!=qrear)//头可能大于尾
    {
        int k=que[qhead++];
        qhead%=maxn;//循环队列
        alpha[k]=0;
        for (int q=head[k] ; ~q ; q=edge[q].next)
            if(edge[q].w)
                if(dis[k]+edge[q].c<dis[edge[q].v])
                {
                    dis[edge[q].v]=dis[k]+edge[q].c;
                    pre[edge[q].v]=q;
                    if(!alpha[edge[q].v])
                    {
                        alpha[edge[q].v]=true;
                        if(edge[q].c<0)
                        {
                            qhead=(qhead-1+maxn)%maxn;
                            que[qhead]=edge[q].v;
                        }
                        else
                        {
                            que[qrear++]=edge[q].v;
                            qrear%=maxn;
                        }
                    }
                }
    }
    if(dis[e]==inf)return -1;
    //终点不可达,返回-1;
    int k=inf;
    for(int i=e ; i!=s ; i=edge[pre[i]^1].v)
        k=min(k,edge[pre[i]].w);
    //sum+=k;//sum记录最大流(有些题里会用到)
    return k;//返回该可行流流量
}

int mcmf(int s,int t)
{
    int ans=0,k;
    while (~(k=spfa(s,t)))
    {
        for (int i=t ; i!=s ; i=edge[pre[i]^1].v)
        {
            edge[pre[i]].w-=k;
            edge[pre[i]^1].w+=k;
        }//更新流
        ans+=dis[t]*k;//最小费用*流量
    }
    return ans;
}

int main ()
{
    int n,m;
    int u,v,c;
    while (~scanf("%d%d",&n,&m))
    {
        memset (head , -1 , sizeof(head));
        cnt=0;
        for(int i=0 ; i<m ; ++i)
        {
            scanf("%d%d%d",&u,&v,&c);
            addedge(u,v,1,c);
            addedge(v,u,1,c);
        }
        addedge(0,1,2,0);
        addedge(n,n+1,2,0);
        int ans=mcmf(0,n+1);
        printf("%d\n",ans);
    }
    return 0;
}


 

#include <cstdio>
#include <cstring>
#define min(a,b) (a>b?b:a)

const int maxn=1005;
const int maxm=60050;
const int inf=0x4fffffff;
struct Edge{
    int v,next,c,w;
}edge[maxm];
int head[maxn],cnt;

void addedge(int u,int v,int w,int c)
{
    edge[cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].c=c;
    edge[cnt].next=head[u];
    head[u]=cnt++;

    edge[cnt].v=u;
    edge[cnt].w=0;
    edge[cnt].c=-c;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}

int dis[maxn],pre[maxn];//最小费用和前驱结点
int alpha[maxn];//标记
int que[maxn],qhead,qrear;

int spfa (int s,int e)//源汇点
{
    //寻找费用增广,没有返回-1;
    for (int i=0 ; i<maxn ; ++i)
        dis[i]=inf;
    memset (alpha , 0 , sizeof(alpha));
    dis[s]=0;
    que[qhead=0]=s;
    qrear=1;
    alpha[s]=1;
    while (qhead!=qrear)//头可能大于尾
    {
        int k=que[qhead++];
        qhead%=maxn;//循环队列
        alpha[k]=0;
        for (int q=head[k] ; ~q ; q=edge[q].next)
            if(edge[q].w && dis[k]+edge[q].c<dis[edge[q].v])
            {
                dis[edge[q].v]=dis[k]+edge[q].c;
                pre[edge[q].v]=q;
                if(!alpha[edge[q].v])
                {
                    alpha[edge[q].v]=true;
                    if(edge[q].c<0)
                    {
                        qhead=(qhead-1+maxn)%maxn;
                        que[qhead]=edge[q].v;
                    }
                    else
                    {
                        que[qrear++]=edge[q].v;
                        qrear%=maxn;
                    }
                }
            }
    }
    if(dis[e]==inf)return -1;
    //终点不可达,返回-1;
    int k=inf;
    for(int i=e ; i!=s ; i=edge[pre[i]^1].v)
        k=min(k,edge[pre[i]].w);
    //sum+=k;//sum记录最大流(有些题里会用到)
    return k;//返回该可行流流量
}

int mcmf(int s,int t)
{
    int ans=0,k;
    while (~(k=spfa(s,t)))
    {
        for (int i=t ; i!=s ; i=edge[pre[i]^1].v)
        {
            edge[pre[i]].w-=k;
            edge[pre[i]^1].w+=k;
        }//更新流
        ans+=dis[t]*k;//最小费用*流量
    }
    return ans;
}

int main ()
{
    int n,m;
    int u,v,c;
    while (~scanf("%d%d",&n,&m))
    {
        memset (head , -1 , sizeof(head));
        cnt=0;
        for(int i=0 ; i<m ; ++i)
        {
            scanf("%d%d%d",&u,&v,&c);
            addedge(u,v,1,c);
            addedge(v,u,1,c);
        }
        addedge(0,1,2,0);
        addedge(n,n+1,2,0);
        int ans=mcmf(0,n+1);
        printf("%d\n",ans);
    }
    return 0;
}


 

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值