BZOJ2324 ZJOI2011 营救皮卡丘 最短路+费用流

题意:给定一张无向图,有K个人,每一时刻K个人可以同时走(也可以停在一个节点),在到达i之前必须先到达i-1,求从0到N,K个人走的最小距离和(只需一个人到达即可)

题解:

用Floyd跑出任意两个城市i j间的最短路,更新的前提是k<j(要到达城市j必须先到达1->j-1)

将每个城市拆成两个点A B,u v间连费用为w的边,i为任意一个城市,按如下方式建图:

从A向B连流量为INF费用为0的边,表示一个城市可以经过多次

从S向0B连流量为K费用为0的边,表示最初有K个人从0出发

从iB向T连流量为1费用为0的边,表示每个城市必须经过一次

从uB向vA连流量为INF费用为w的边,表示一条边可以经过多次

从S向iB连流量为1费用为0的边,理由:最大流的流量一定为N+1,但流出量只有K,因此可能有的人需要走多个城市,向每个城市连边表示可以将任意一个城市当作起点重新走。

然后跑S到T的最大流即可。末了的结论就是,最大流的流量就是为了限制方案数的。

#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <climits>
#include <iostream>
#include <algorithm>
using namespace std;
#define S (2*N+3)
#define T (2*N+4)
#define U 1000000000

const int MAXN=200+2;
const int MAXV=1000+2;
const int MAXM=600000+2;
struct HASH{
    int u;
    HASH *next;
    HASH(){}
    HASH(int _u,HASH *_next):u(_u),next(_next){}
}*table[MAXV],mem[MAXM];
struct EDGE{
    int u,v,c,w;
    EDGE(){}
    EDGE(int _u,int _v,int _c,int _w):u(_u),v(_v),c(_c),w(_w){}
}e[MAXM];
int N,M,K,dist[MAXN][MAXN],d[MAXV],cur[MAXV],ans,cnt=2;
bool flag[MAXV];
queue<int> q;

void Floyd(){
    for(int k=0;k<=N;k++)
    for(int i=0;i<=N;i++)
    for(int j=0;j<=N;j++)
        if((k<=i || k<=j) && dist[i][j]>dist[i][k]+dist[k][j])
            dist[i][j]=dist[i][k]+dist[k][j];
}

void Insert(int u,int v,int c,int w){
    table[u]=&(mem[cnt]=HASH(cnt,table[u])),e[cnt++]=EDGE(u,v,c,w);
    table[v]=&(mem[cnt]=HASH(cnt,table[v])),e[cnt++]=EDGE(v,u,0,-w);
}

bool SPFA(int s,int t){
    for(int i=0;i<=t;i++) d[i]=U;
    d[s]=0,flag[s]=1,q.push(s);

    int x;
    while(!q.empty()){
        x=q.front(),q.pop();
        for(HASH *p=table[x];p;p=p->next)
            if(e[p->u].c && d[e[p->u].v]>d[x]+e[p->u].w){
                d[e[p->u].v]=d[x]+e[p->u].w,cur[e[p->u].v]=p->u;
                if(!flag[e[p->u].v]) flag[e[p->u].v]=1,q.push(e[p->u].v);
            }
        flag[x]=0;
    }
    return d[t]<U;
}

int Find(int s,int t){
    int ret=0,c=INT_MAX;
    for(int i=cur[t];i;i=cur[e[i].u]) c=min(c,e[i].c);
    for(int i=cur[t];i;i=cur[e[i].u]){
        e[i].c-=c,e[i^1].c+=c;
        ret+=e[i].w*c;
    }
    return ret;
}

int main(){
    scanf("%d %d %d",&N,&M,&K);
    for(int i=0;i<=N;i++)
        for(int j=0;j<=N;j++)
            if(i!=j) dist[i][j]=U;
    for(int i=1,u,v,w;i<=M;i++){
        scanf("%d %d %d",&u,&v,&w);
        dist[u][v]=dist[v][u]=min(w,dist[u][v]);
    }
    Floyd();

    for(int i=1;i<=N;i++) Insert(S,i+N+1,1,0),Insert(i,T,1,0);
    Insert(S,N+1,K,0);
    for(int i=0;i<=N;i++)
        for(int j=i+1;j<=N;j++)
            if(dist[i][j]<U) Insert(i+N+1,j,1,dist[i][j]);

    while(SPFA(S,T)) ans+=Find(S,T);
    printf("%d\n",ans);

    return 0;
}
View Code

 

posted @ 2017-02-28 23:10  WDZRMPCBIT  阅读(169)  评论(0编辑  收藏  举报