1. 程式人生 > >【BZOJ】2324: [ZJOI2011]營救皮卡丘-MCMF

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

傳送門: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;
    
}