费用流模板

最小费用最大流:(SPFA求最长路)(下面是一个求二分图最小权重的例题)

375. 蚂蚁

将白黑点左右分部,连接所有边,这道题等价于求总边权最小。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;

typedef long long ll;

const int maxn=609;

const int maxm=2e5+7;

const double epos=1.0e-7;
const int inf=0x3f3f3f3f;

struct Edge{
    int v,w,next;
    double cost;//花费;
}edge[maxm];

int head[maxn],top;

void init(){
    top=0;
    memset(head,-1,sizeof(head));
}

void add(int u,int v,int w,double cost){
    edge[top].v=v;
    edge[top].w=w;
    edge[top].cost=cost;
    edge[top].next=head[u];
    head[u]=top++;
}

double d[maxn];
int incf[maxn];
int pre[maxn];//记录前驱,便于找到最长路的实际方案;
bool vis[maxn];
int s,t;
int maxflow;//最大流;
double ans;//最小费用;
int n,m;
queue<int> q;
bool spfa(){
    while(!q.empty()) q.pop();
    for(int i=0;i<=2*n+1;++i) d[i]=inf;
    memset(vis,0,sizeof(vis));
    q.push(s);
    d[s]=0,vis[s]=1;
    incf[s]=inf;
    int u,v;
    int w;
    while(q.size()){
        u=q.front(); q.pop();
        vis[u]=0;
        for(int i=head[u];i!=-1;i=edge[i].next){
            v=edge[i].v;
            w=edge[i].w;
            if(!w) continue;
            if(d[v]>d[u]+edge[i].cost){
                d[v]=d[u]+edge[i].cost;
                incf[v]=min(incf[u],w);
                pre[v]=i;
                if(!vis[v]) vis[v]=1,q.push(v);
            }
        }
    }
    return d[t]<inf-epos;
}

void updata(){
    int u=t;
    while(u!=s){
        int i=pre[u];
        edge[i].w-=incf[t];
        edge[i^1].w+=incf[t];
        u=edge[i^1].v;
    }
    maxflow+=incf[t];
    ans+=d[t]*incf[t];
}

int x1[maxn],y111[maxn],x2[maxn],y2[maxn];

double juli(double x1,double y111,double x2,double y2){
    return sqrt((x1-x2)*(x1-x2)+(y111-y2)*(y111-y2));
}

int main(){
    scanf("%d",&n);
    s=0,t=2*n+1;
    ans=maxflow=0;
    memset(incf,0,sizeof(incf));

    init();
    for(int i=1;i<=n;++i) add(s,i,1,0),add(i,s,0,0),add(i+n,t,1,0),add(t,i+n,0,0);
    for(int i=1;i<=n;++i) scanf("%d%d",&x1[i],&y111[i]);
    for(int i=1;i<=n;++i) scanf("%d%d",&x2[i],&y2[i]);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            add(i,j+n,1,juli(x1[i],y111[i],x2[j],y2[j])),add(j+n,i,0,-juli(x1[i],y111[i],x2[j],y2[j]));

    while(spfa()) updata();

    for(int i=1;i<=n;++i)
        for(int j=head[i];j!=-1;j=edge[j].next)
            if(edge[j].w==0){
                printf("%d\n",edge[j].v-n);
                break;
            }

    return 0;
}

最大费用的话,为求最短路,将d数组设为负无穷,同时spfa中的松弛条件改为<,return的部分改为与负无穷的差值。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值