【BZOJ】2324: [ZJOI2011]营救皮卡丘-MCMF

版权声明:欢迎转载(请附带原链接)ヾ(๑╹◡╹)ノ" https://blog.csdn.net/corsica6/article/details/84403181

传送门:bzoj2324


题解

首先 F l o y d Floyd 处理出每个点对 ( i , j ) (i,j) 在不经过任何标号大于 m a x ( i , j ) max(i,j) 的点时的最短路 d [ i ] [ j ] d[i][j] ,便求出了一个点到另一个点的花费。

摧毁一个点是一次性的,所以将问题转换为将 1 n 1-n 分成 k k 组递增数列 P i = { a i , 1 , a i , 2 , . . , a i , P } P_i=\{a_{i,1},a_{i,2},..,a_{i,|P|}\} ,满足 i = 1 k j = 2 P i d [ a i , j 1 ] [ a i , j ] \sum\limits_{i=1}^k \sum\limits_{j=2}^{|P_i|}d[a_{i,j-1}][a_{i,j}] 最小。类似于 D A G DAG 的最小路径覆盖。

考虑拆点(入度 i i 出度 i i' )构图,满足 0 0 号点出度为 k k 1 ( n 1 ) 1-(n-1) 号点出入度均为1, n n 号点入度为1,时的MCMF。

1 ( n 1 ) 1-(n-1) 号点的向汇点连一条流量为 1 1 ,费用为 0 0 的边。(入度)

源点向 0 0' 连一条流量为 k k ,费用为 0 0 的边,向 1 ( n 1 ) 1'-(n-1)' 分别连一条流量为 1 1 ,费用为 0 0 的边。(出度)

每个点对 ( i , j ) , ( i < j ) (i,j),(i<j) ,从 i i' j j 连一条流量为 1 1 ,费用为 d [ i ] [ j ] d[i][j] 的边。

M C M F MCMF 的花费即为答案。

p.s. 因为出度比较好限制所以和源点连,入度只能拿来判断所以和汇点连。


代码

#include<bits/stdc++.h>
using namespace std;
const int N=400,M=1e6+10,inf=0x3f3f3f3f;
typedef long long ll;

int n,m,K,S,T,d[160][160],vs[N],dis[N],tim;
int cur[N],head[N],to[M],nxt[M],w[M],cc[M],tot=1;
ll tot_cost;bool inq[N];

inline void lk(int u,int v,int flw,int cst)
{
    to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=flw;cc[tot]=cst;
    to[++tot]=u;nxt[tot]=head[v];head[v]=tot;w[tot]=0;cc[tot]=-cst;
}

inline void dn(int &x,int y){if(y<x) x=y;}

deque<int>que;
inline bool spfa()
{
    memset(dis,0x3f,sizeof(dis));
    int i,j,x;
    dis[T]=0;que.push_back(T);inq[T]=true;
    for(;que.size();){
        x=que.front();que.pop_front();
        for(i=head[x];i;i=nxt[i]){
            j=to[i];if((!w[i^1])||(dis[j]<=dis[x]-cc[i])) continue;
            dis[j]=dis[x]-cc[i];if(inq[j]) continue;
            if(que.empty()||(dis[j]<=dis[que.front()])) que.push_front(j);
            else que.push_back(j);inq[j]=true;
        }
        inq[x]=false;
    }
    return dis[S]<inf;
}

int dfs(int x,int f)
{
    vs[x]=tim;
    if(x==T) return f;
    int j,res,ss=0;
    for(int &i=cur[x];i;i=nxt[i]){
        j=to[i];if((!w[i])||(dis[j]!=dis[x]-cc[i])||(vs[j]==tim)) continue;
        res=dfs(j,min(w[i],f-ss));if(!res) continue;
        w[i]-=res;w[i^1]+=res;tot_cost+=(ll)cc[i]*res;
        ss+=res;if(ss==f) return ss;
    }
    if(!ss) dis[x]=-1;
    return ss;
}

int main(){
    memset(d,0x3f,sizeof(d));
    int i,j,k,x,y,z;
    scanf("%d%d%d",&n,&m,&K);n++;
    S=(n<<1)+1;T=S+1;
    for(i=1;i<=n;++i) d[i][i]=0;
    for(i=1;i<=m;++i){
        scanf("%d%d%d",&x,&y,&z);
        x++;y++;
        dn(d[x][y],z);dn(d[y][x],z);
    }
    for(k=1;k<=n;++k)
     for(i=1;i<=n;++i)
      for(j=1;j<=n;++j)
       if((k<=i||k<=j))
       dn(d[i][j],d[i][k]+d[k][j]);
    lk(S,1+n,K,0);
    for(i=2;i<=n;++i) lk(S,i+n,1,0),lk(i,T,1,0);
    for(i=1;i<n;++i)
     for(j=i+1;j<=n;++j) 
       if(d[i][j]<inf)
        lk(i+n,j,1,d[i][j]);
    for(;spfa();)
        for(vs[T]=tim;vs[T]==tim;){
            memcpy(cur,head,sizeof(cur));
            ++tim;dfs(S,inf);
        }
    printf("%lld",tot_cost);
    return 0;
    
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/84403181