bzoj千题计划159:bzoj2055: 80人环游世界(有源汇上下界可行最小费用流)

http://www.lydsy.com/JudgeOnline/problem.php?id=2055

 

某个国家必须经过vi次,

可以转化为上下界都为vi的边

 

对这张图做有源汇上下界可行最小费用流

 按无源汇上下界可行流建好图,跑超级源点到超级汇点的最小费用最大流即可

 

 
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
 
using namespace std;
 
#define N 205
#define M 10500
 
const int inf=1e9;
 
int src,decc;
int S,T;
 
int tot=1;
int front[N],to[M<<1],nxt[M<<1],cap[M<<1],val[M<<1],from[M<<1];
 
int cost;
 
int dis[N];
bool vis[N];
 
void read(int &x)
{
    x=0;  int f=1; char c=getchar();
    while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    x*=f;
}
 
void add(int u,int v,int w,int f)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; from[tot]=u; cap[tot]=w; val[tot]=f;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; from[tot]=v; cap[tot]=0; val[tot]=-f; 
}
 
int agument(int now,int flow)
{
    vis[now]=true;
    if(now==T)
    {
        cost-=dis[S]*flow;
        return flow;
    }
    int delta;
    for(int i=front[now];i;i=nxt[i])
    {
        if(cap[i] && !vis[to[i]] && dis[to[i]]==dis[now]+val[i])
        {
            delta=agument(to[i],min(flow,cap[i]));
            if(delta)
            {
                cap[i]-=delta;
                cap[i^1]+=delta;
                return delta;
            }
        }
    }
    return 0;
}
 
bool retreat()
{
    if(vis[T]) return true;
    int mi=inf;
    for(int i=2;i<=tot;++i)
        if(cap[i] && vis[from[i]] && !vis[to[i]])
            mi=min(mi,dis[from[i]]+val[i]-dis[to[i]]);
    if(mi==inf) return false;
    for(int i=0;i<=T;++i)
        if(vis[i]) dis[i]-=mi;
    return true; 
}
 
void zkw()
{
    do
    {
        memset(vis,false,sizeof(vis));
        agument(S,inf);
    }while(retreat());
    cout<<cost;
}
 
int main()
{
    int n,m;
    read(n); read(m);
    src=0; decc=(n<<1|1)+1;
    int ss=1; 
    S=decc+1; T=S+1;
    add(src,T,m,0);
    add(S,ss,m,0);
    int x;
    for(int i=1;i<=n;++i) 
    {
        read(x);
        add(ss,i<<1,m,0);
        add(i<<1|1,decc,m,0);
        add(i<<1,T,x,0);
        add(S,i<<1|1,x,0);
    }
    int k;
    for(int i=1;i<n;++i)
    {
        for(int j=i+1;j<=n;++j) 
        {
            read(x);
            if(x==-1) continue;
            add(i<<1|1,j<<1,m,x);
        }
    }
    add(decc,src,m,0);
    zkw();
} 

 

转载于:https://www.cnblogs.com/TheRoadToTheGold/p/8087597.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值