BZOJ 1927: [Sdoi2010]星际竞速 [最小费用最大流]

本文详细解析了如何使用最小费用流算法解决特定问题,并通过具体的代码实现展示了算法的应用过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面传送门

题解

类似DAG上的最小路径覆盖

最小费用流,建图 令源点为 S ,汇点为T:

S向所有入点连容量为1,费用为0的边

S向所有出点连容量为1,费用为 ai 的边

所有出点与T连容量为1,费用为0的边

对于有边的 (ui,vi) 其中 ui<vi ,从 ui 的入点连向 vi 的出点容量为1,费用为 wi 的边

理解一下:相当于如果要到 T 就要走到出点 就要从入点来或者S过来,费用分别对应高速航行和瞬移

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define N 2*805
#define M 15005
using namespace std;

struct edge{int to,nxt,v,c;}e[M<<3];
int n,m,cnt,s,t,head[N],dis[N],from[N],pre[N];
bool v[N];

inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline int read(){
    int a=0;char f=1,c=nc();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=nc();}
    while(c>='0'&&c<='9'){a=a*10+c-'0';c=nc();}
    return a*f;
}

inline void add(int x,int y,int v,int c){
    e[++cnt]=(edge){y,head[x],v,c};
    head[x]=cnt;
    e[++cnt]=(edge){x,head[y],0,-c};
    head[y]=cnt;
}

bool spfa(){
    queue<int>q;
    memset(dis,0x3f3f3f3f,sizeof(dis));
    memset(from,-1,sizeof(from));
    q.push(s);
    v[0]=1;dis[s]=0;
    while(!q.empty()){
        int u=q.front();
        q.pop(),v[u]=0;
        for(int i=head[u];i!=-1;i=e[i].nxt)
            if(e[i].v&&dis[u]+e[i].c<dis[e[i].to]){
                dis[e[i].to]=dis[u]+e[i].c;
                from[e[i].to]=u,pre[e[i].to]=i;
                if(!v[e[i].to]) q.push(e[i].to),v[e[i].to]=1;
            }
    }
    return from[t]!=-1;
}

int main(){
    memset(head,-1,sizeof(head));
    n=read(),m=read();
    s=0,t=(n<<1)+1;
    cnt=-1;
    for(int i=1,x;i<=n;++i){
        x=read();
        add(s,n+i,1,x);
        add(s,i,1,0);
        add(n+i,t,1,0);
    }
    for(int i=1,x,y,z;i<=m;++i){
        x=read(),y=read(),z=read();
        if(x>y) swap(x,y);
        add(x,n+y,1,z);
    }
    int cost=0;
    while(spfa()){
        int f=5;
        for(int i=t;i!=s;i=from[i])
            f=min(f,e[pre[i]].v);
        for(int i=t;i!=s;i=from[i]){
            e[pre[i]].v-=f;
            e[pre[i]^1].v+=f;
        }
        cost+=f*dis[t];
    }
    printf("%d\n",cost);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值