【洛谷P4553】80人环游世界

上下界费用流

题目大意:

有 $n$ 个城市,$m$ 个人旅游。两个城市 $i$ 和 $j$,如果 $i<j$ 那么可能有一条从 $i$ 到 $j$ 的路径,且有一定费用。

每个人可以从任意城市出发,走到任意城市结束。

已知城市 $i$ 被经过了 $V_i$ 次(出发和结束也算)。

问最小总费用。

题解:

限制了每个城市的经过次数,可以转化为上下界费用流。

对每个城市拆成入点 $x_i$和出点 $y_i$。新建源点 $S$,汇点 $T$,辅助汇点 $t$。

建图方法如下:

$x_i$ 向 $y_i$ 连容量 $[V_i,V_i]$,费用 $0$ 的边。

$S$ 向 $x_i$ 连容量 $[0,+\infty]$,费用 $0$ 的边。

$y_i$ 向 $t$ 连容量 $[0,+\infty]$,费用 $0$ 的边。

两个有边的点 $i,j$,$y_i$ 向 $x_j$ 连容量 $[0,+\infty]$,费用为边的花费的边。

$t$ 向 $T$ 连容量 $[m,m]$,费用 $0$ 的边。这里表示要恰好 $m$ 个人。

然后跑最小费用可行流即可。

Code:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int M=123456,N=1234,inf=0x3fffffff;
int n,m,S,t,T,TT,head[N],cnt=1,iter[N],dis[N],dlt[N];
int ans;
bool vis[N];
queue<int>q;
struct edge{
    int to,nxt,cap,cost;
}e[M];
inline void addedge(int u,int v,int cap,int cost){
    e[++cnt]=(edge){v,head[u],cap,cost},head[u]=cnt;
    e[++cnt]=(edge){u,head[v],0,-cost},head[v]=cnt;
}
inline void AddEdgE(int u,int v,int L,int R,int cost){
    ans+=L*cost;dlt[u]-=L,dlt[v]+=L;
    addedge(u,v,R-L,cost);
}
int dfs(int u,int f){
    if(!f||u==TT)return f;
    vis[u]=1;
    int ret=0;
    for(int&i=iter[u];i;i=e[i].nxt)
        if(e[i].cap&&!vis[e[i].to]&&dis[e[i].to]==dis[u]+e[i].cost){
            int d=dfs(e[i].to,min(e[i].cap,f));
            e[i].cap-=d,e[i^1].cap+=d,f-=d,ret+=d;
            if(!f)break;
        }
    vis[u]=0;
    return ret;
}
bool MCMF(int&cost){
    memset(dis,0x3f,sizeof dis);
    dis[0]=0;
    for(q.push(0);!q.empty();){
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=e[i].nxt)
            if(e[i].cap&&dis[e[i].to]>dis[u]+e[i].cost){
                dis[e[i].to]=dis[u]+e[i].cost;
                if(!vis[e[i].to])q.push(e[i].to),vis[e[i].to]=1;
            }
    }
    if(dis[TT]>1e9)return 0;
    memcpy(iter,head,sizeof head);
    cost+=dis[TT]*dfs(0,inf);
    return 1;
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;++i){
        int V;
        cin>>V;
        AddEdgE(i,i+n,V,V,0);
    }
    t=2*n+2,T=2*n+3,TT=2*n+4,S=2*n+1;
    AddEdgE(t,T,m,m,0);
    for(int i=1;i<=n;++i){
        AddEdgE(S,i,0,m,0);
        AddEdgE(i+n,t,0,m,0);
        for(int j=i+1,x;j<=n;++j){
            cin>>x;
            if(x>=0)AddEdgE(i+n,j,0,m,x);
        }
    }
    addedge(T,S,inf,0);
    for(int i=1;i<=T;++i)
        if(dlt[i]>0)addedge(0,i,dlt[i],0);else
            if(dlt[i]<0)addedge(i,TT,-dlt[i],0);
    while(MCMF(ans));
    cout<<ans<<'\n';
    return 0;
}