zoukankan      html  css  js  c++  java
  • [SCOI2007][bzoj1070] 修车 [费用流]

    题面

    传送门

    思路

    我们考虑某个工人修车的从前到后序列如下:

    ${W_1,W_2,W_3,...,W_n}$

    那么,对于这n辆车的车主而言,他们等候的总时间为:

    $sum_{i=1}^{n}W_iastleft(n-i+1 ight)=nW_1+left(n-1 ight)W_2+...+2W_{n-1}+W_n$

    这一步很重要,因为经过这一步推导,我们发现:对于“把第i辆车让第j个人在“需要消耗k次时间”的那个个位置修”这一个决策,可以等同为进行一个费用为$Tleft(i,j ight)ast k$的决策

    因此我们可以得到一个决策集合:决策$left(i,j,k ight)=Tleft(i,j ight)ast k$表示“把第i辆车让第j个人在“需要消耗k次时间”的那个个位置修”

    那实际上我们就是对于每个i选取一个这样的决策,同时这个决策的$left(j,k ight)$不能相同

    那就好办了,我们建立一个二分图,左边是n辆车,右边是n*m个上述状态的二元组$left(j,k ight)$(可以证明$kleq n$)

    源点向车连边,而决策二元组向汇点连边,流量1费用0

    中间的边(注意这是一个完全二分图)就是流量1费用$Tleft(i,j ight)ast k$的

    跑最小费用最大流即可

    Code:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define inf 1e9
    #define id(i,j) (i-1)*n+j
    using namespace std;
    inline int read(){
        int re=0,flag=1;char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-') flag=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
        return re*flag;
    }
    int n,m,cnt=-1,ans,first[1010],dis[1010],vis[1010];
    struct edge{
        int to,next,w,cap;
    }a[100010];
    inline void add(int u,int v,int w,int cap){
        a[++cnt]=(edge){v,first[u],w,cap};first[u]=cnt;
        a[++cnt]=(edge){u,first[v],-w,0};first[v]=cnt;
    }
    bool spfa(int s,int t){
        int q[5010],head=0,tail=1,u,v,w,i;
        memset(dis,-1,sizeof(dis));memset(vis,0,sizeof(vis));
        q[0]=t;vis[t]=1;dis[t]=0;
        while(head<tail){
            u=q[head++];vis[u]=0;
            for(i=first[u];~i;i=a[i].next){
                v=a[i].to;w=a[i].w;
                if(a[i^1].cap&&((dis[v]==-1)||(dis[v]>dis[u]-w))){
                    dis[v]=dis[u]-w;
                    if(!vis[v]) vis[v]=1,q[tail++]=v;
                }
            }
        }
        return ~dis[s];
    }
    int dfs(int u,int t,int limit){
        if(u==t||!limit){vis[u]=1;return limit;}
        int i,v,f,flow=0,w;vis[u]=1;
        for(i=first[u];~i;i=a[i].next){
            v=a[i].to;w=a[i].w;
            if(!vis[v]&&a[i].cap&&(dis[v]==dis[u]-w)){
                if(!(f=dfs(v,t,min(limit,a[i].cap)))) continue;
                a[i].cap-=f;a[i^1].cap+=f;
                ans+=f*w;flow+=f;limit-=f;
                if(!limit) return flow;
            }
        }
        return flow;
    }
    int zkw(int s,int t){//zkw费用流
        int re=0;
        while(spfa(s,t)){
            vis[t]=1;
            while(vis[t]){
                memset(vis,0,sizeof(vis));
                re+=dfs(s,t,inf);
            }
        }
        return re;
    }
    int main(){
        memset(first,-1,sizeof(first));int i,j,k,t1;
        m=read();n=read();
        for(i=1;i<=n;i++) add(0,i,0,1);
        for(i=1;i<=m;i++) for(j=1;j<=n;j++) add(n+id(i,j),n+n*m+1,0,1);
        for(i=1;i<=n;i++){
            for(j=1;j<=m;j++){
                t1=read();
                for(k=1;k<=n;k++){
                	add(i,n+id(j,k),t1*k,1);
    			}
            }
        }
        zkw(0,n+n*m+1);
        printf("%.2lf",((double)ans)/((double)n));//注意输出保留两位小数
    }
    
  • 相关阅读:
    VirtualBox的四种网络连接方式详解
    need to be root
    Unreachable catch block for IOException. This exception is never thrown from the try statement body
    git fetch 拉取而不合并
    IOS开发的哪些异常之异常断点
    duplicate报ORA-01017权限问题
    Woody的Python学习笔记4
    微软100题第51题:和为n连续正数序列
    C语言scanf函数详解
    火星人乘坐核动力飞船回故乡
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/8733196.html
Copyright © 2011-2022 走看看