HYSBZ — 1070 修车(最小费用最大流)

题目大意

同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小。 说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间。

思路分析:

①:设立一个源点,连向每一辆车,容量为1,费用为0;

②:让每辆车i连向技术人员j,把技术人员拆成n部分,每条边容量为1,费用为(n-k+1)*time[i][j],(k代表一个技术人员被拆成的第几个点,也就代表当前这辆车被这个维修人员第几个修);

③:让n*m个技术人员连向汇点,容量为1,费用为0;

这道题要注意的是顾客的等待时间不光是自己维修所需的时间,还有等待别人维修的时间(用一同一位技术人员的),刚开始是这样建图的,设立一个源点,连向n辆车,再用n两车连向m个技术人员,然后每次找到一个能扩增的流,就在此条路径车连向汇点的边加上此次的花费,我想这样就相当于加上等待别人维修的时间了,可是这样想是不对的,我想到了一个这样的样例 2 2---3 2---8 4,两辆车,两位技术人员,如果按照我此时的做法,那么顾客的平均等待时间就是4.00,第二位技术人员先修理第一辆车,第二辆车等着第一辆修完了再修理,而实际上,只需要3.50,第一位技术人员修理第一辆车,同时第二位技术人员修理第二辆车。。。而这时候应该怎么做呢,(此时参考了别人的思想。。。)我们可以把每个技术人员分成n个人,代表一个人可以修n辆车,对于一辆车i,可以被一位技术人员j,第一个修,第二个修。。。第n个修,那么如果它被修,别人如果也用这个技术人员,就需要等待,等待的时间也就是(n-k+1)*time[i][j] (k代表第几个修,后面也就有n-k个人等待,要算上自己的时间啊。。) 。于是建立这样一个图,设立一个源点s,连向每一辆车,容量为1,费用为0,再把每辆车连向每位技术人员(每个有n个点,对应第几个修,容量为1,费用为上面的式子),最后,再让m*n个技术人员连向汇点,容量为1,费用为0;

说一个样例,正是通过这个样例让我突然理解为什么我们在加边的时候,反向边的费用为什么是-cost:


在第一次循环的时候,走的路线是s-->1-->6-->t 费用是2,也就是第一辆车被第二个技术人员修理,其实这不是我们理想的,我们理想的是,第一辆车被第一个技术人员修理,这样才能同时第二辆车被第二个技术人员修理,别着急,往下看,第二次循环的时候,0-->1,6-->t 这两条路走过了不能走(图中画x),我们先是从s-->2,2-->6,然后我们就沿着6-->1(原本容量为0,费用为-2,在走过1-->6之后,容量也就为1,费用为0) 然后dis[1]=dis[6]+edge[].cost=4+(-2)=2,继而,又从1-->4->t,费用为5,看到这里,有没有发现什么,是不是最后的结果就好像1-->6这条边没有被走过,来回走了两遍,得用一正一负抵消了,然后好像走的就是s-->1-->4-->t,s-->2-->6-->t,这和我们理想的就是一样的了,太神奇了。。。。

由于不会画图,就只能在纸上画图拍照了,嘻嘻 ,这就是我对这道题的全部理解了,希望看到我这篇文章的人不要笑啊。。。嘻嘻嘻

代码实现:

zkw:600ms

#include<cstdio>
#include<cstring>
#include<iostream>
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int N=700;
const int M=74320;
const int INF=0x3f3f3f3f;
int n,m,s,t,top,head[N],vis[N],tim[65][15],cost,ans;
struct Edge{
    int to,next,flow,cost;
    Edge(int _to=0,int _next=0,int _flow=0,int _cost=0):to(_to),next(_next),flow(_flow),cost(_cost){}
}edge[M];

void Addedge(int from,int to,int flow,int cost){
    edge[top]=Edge(to,head[from],flow,cost);
    head[from]=top++;
    edge[top]=Edge(from,head[to],0,-cost);
    head[to]=top++;
}

int aug(int u,int f){
    if(u==t){
        ans+=cost*f;
        return f;
    }
    vis[u]=1;
    int tmp=f;
    for(int i=head[u];i!=-1;i=edge[i].next){
        if(edge[i].flow&&!edge[i].cost&&!vis[edge[i].to]){
            int delta=aug(edge[i].to,Min(tmp,edge[i].flow));
            edge[i].flow-=delta;
            edge[i^1].flow+=delta;
            tmp-=delta;
            if(!tmp) return f;
        }
    }
    return f-tmp;
}

bool modlabel(){
    int delta=INF;
    for(int u=s;u<=t;u++){
        if(vis[u]){
            for(int i=head[u];i!=-1;i=edge[i].next)
                if(edge[i].flow&&!vis[edge[i].to]&&edge[i].cost<delta) delta=edge[i].cost;
        }
    }
    if(delta==INF) return 0;
    for(int u=s;u<=t;++u){
        if(vis[u]){
            for(int i=head[u];i!=-1;i=edge[i].next)
                edge[i].cost-=delta,edge[i^1].cost+=delta;
        }
    }
    cost+=delta;
    return 1;
}

void costflow(){
    do{
        do{
            memset(vis,0,sizeof(vis));
        }while(aug(s,INF));
    }while(modlabel());
}

int main(){
    while(~scanf("%d%d",&m,&n)){
        memset(head,-1,sizeof(head));
        top=ans=cost=0;
        s=0,t=n*m+n+1;
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j) scanf("%d",&tim[i][j]);
        for(int i=1;i<=n;++i) Addedge(s,i,1,0);
        for(int i=1;i<=n;++i){
            for(int j=1;j<=m;++j){
                for(int k=1;k<=n;++k){
                    Addedge(i,j*n+k,1,k*tim[i][j]);
                }
            }
        }
        for(int i=1;i<=m;++i)
            for(int j=1;j<=n;++j) Addedge(i*n+j,t,1,0);
        costflow();
        double res=ans*(1.0)/n;
        printf("%.2lf\n",res);
    }
}

spfa:784ms

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int N=700;
const int M=74320;
const int INF=0x3f3f3f3f;
int n,m,s,t,top,head[N],vis[N],sum_cost,ans,dis[N],pre[N],minflow[N],path[N],tim[65][15];
struct Edge{
    int to,next,flow,cost;
    Edge(int _to=0,int _next=0,int _flow=0,int _cost=0):to(_to),next(_next),flow(_flow),cost(_cost){}
}edge[M];

void Addedge(int from,int to,int flow,int cost){
    edge[top]=Edge(to,head[from],flow,cost);
    head[from]=top++;
    edge[top]=Edge(from,head[to],0,-cost);
    head[to]=top++;
}

int Spfa(){
    queue<int> q;
    memset(dis,0x3f,sizeof(dis));
    memset(minflow,0x3f,sizeof(minflow));
    memset(pre,-1,sizeof(pre));
    memset(vis,0,sizeof(vis));
    dis[s]=0;
    q.push(s);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i+1;i=edge[i].next){
            if(edge[i].flow&&dis[edge[i].to]>dis[u]+edge[i].cost){
                dis[edge[i].to]=dis[u]+edge[i].cost;
                pre[edge[i].to]=u;
                path[edge[i].to]=i;
                minflow[edge[i].to]=Min(minflow[u],edge[i].flow);
                if(!vis[edge[i].to]){
                    vis[edge[i].to]=1;
                    q.push(edge[i].to);
                }
            }
        }
    }
    if(dis[t]==INF) return 0;
    sum_cost+=minflow[t]*dis[t];
    int u=t;
    while(u!=s){
        edge[path[u]].flow-=minflow[t];
        edge[path[u]^1].flow+=minflow[t];
        u=pre[u];
    }
    return 1;
}

int main(){
    while(~scanf("%d%d",&m,&n)){
        memset(head,-1,sizeof(head));
        top=sum_cost=0;
        s=0,t=n*m+n+1;
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j) scanf("%d",&tim[i][j]);
        for(int i=1;i<=n;++i) Addedge(s,i,1,0);
        for(int i=1;i<=n;++i){
            for(int j=1;j<=m;++j){
                for(int k=1;k<=n;++k){
                    Addedge(i,j*n+k,1,(n-k+1)*tim[i][j]);
                }
            }
        }
        for(int i=1;i<=m;++i)
            for(int j=1;j<=n;++j) Addedge(i*n+j,t,1,0);
        while(Spfa());
        double res=sum_cost*(1.0)/n;
        printf("%.2lf\n",res);
    }
}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值