BZOJ3876/AHOI2014支线剧情

本文介绍了一种利用最小费用流算法解决特定图论问题的方法,即确保图中每条边至少被遍历一次。通过设定适当的流量限制与费用,文章详细展示了如何构建网络流模型,并给出了具体的代码实现,包括数据结构定义、读取输入、网络流求解等步骤。

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

思路:
就是每条边要经过最少一次咯,直接上下界网络流把下届设置成1即可,当然一些关于类似汇点的处理方法细节就写着代码里了。详细的可以看别人的。

/*
貌似直接每条边的流量下界设置成1,直接跑最小费用最大流?
好像没有一个明确的汇点
假设增加一个汇点的话 那么结局点要向汇点连多少的边?
索性不要汇点 直接每个点向源点连inf的边来平衡流量 
*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
using namespace std;
const int imax=300+9;
const int dmax=imax;
const int bmax=200000+229;
const int inf=100000229;
int n,m,S,T; 
int num,head[dmax],from[bmax],to[bmax],inext[bmax],re[bmax],val[bmax];

void iadd(int u,int v,int flow,int nowv){
    to[num]=v; from[num]=u; re[num]=flow; val[num]=nowv;
    inext[num]=head[u]; head[u]=num++; 
}
void add(int u,int v,int flow,int nowv) { iadd(u,v,flow,nowv); iadd(v,u,0,-nowv);}
void iread()
{
    scanf("%d",&n);
    S=0; T=n+1;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&m);
        int u=i,v,nowval;
        for(int i=1;i<=m;i++)
        {
            scanf("%d %d",&v,&nowval);
            //上下界 
            add(S,v,1,nowval);
            add(u,v,inf,nowval);
        }   
        add(u,T,m,0); 
    }   
    for(int i=2;i<=n;i++) add(i,1,inf,0);//代替T->S 的inf 
}

int dist[dmax],vis[dmax],pre[dmax];
queue<int> q;
bool spfa()
{
    for(int i=S;i<=T;i++) vis[i]=false,dist[i]=inf,pre[i]=-1;
    q.push(S); dist[S]=0; vis[S]=1; 
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        vis[u]=false;
        for(int i=head[u];i!=-1;i=inext[i])
            if(re[i] && dist[to[i]]>dist[u]+val[i])
            {
                pre[to[i]]=i;
                dist[to[i]]=dist[u]+val[i];
                if(!vis[to[i]]) { vis[to[i]]=1; q.push(to[i]);}
            }   
    }
    return dist[T]!=inf;        
}

void iwork()
{
    int ans=0; 
    int Maxflow=0;
    while(spfa())
    {
        Maxflow++;
        for(int i=T;i!=S;i=from[pre[i]])
        {
            int k=pre[i];
            ans+=val[k];
            re[k]-=1; re[k^1]+=1;   
        }   
    }   
    printf("%d\n",ans);
}

int main()
{
    iread();
    iwork();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值