• [ACM] POJ 3686 The Windy's (二分图最小权匹配,KM算法,特殊建图)


    The Windy's
    Time Limit: 5000MS   Memory Limit: 65536K
    Total Submissions: 4158   Accepted: 1777

    Description

    The Windy's is a world famous toy factory that owns M top-class workshop to make toys. This year the manager receivesN orders for toys. The manager knows that every order will take different amount of hours in different workshops. More precisely, thei-th order will take Zij hours if the toys are making in thej-th workshop. Moreover, each order's work must be wholly completed in the same workshop. And a workshop can not switch to another order until it has finished the previous one. The switch does not cost any time.

    The manager wants to minimize the average of the finishing time of the N orders. Can you help him?

    Input

    The first line of input is the number of test case. The first line of each test case contains two integers,N and M (1 ≤ N,M ≤ 50).
    The next N lines each contain M integers, describing the matrixZij (1 ≤ Zij ≤ 100,000) There is a blank line before each test case.

    Output

    For each test case output the answer on a single line. The result should be rounded to six decimal places.

    Sample Input

    3
    
    3 4
    100 100 100 1
    99 99 99 1
    98 98 98 1
    
    3 4
    1 100 100 100
    99 1 99 99
    98 98 1 98
    
    3 4
    1 100 100 100
    1 99 99 99
    98 1 98 98
    

    Sample Output

    2.000000
    1.000000
    1.333333
    
    

    Source

    解题思路:

    题意为有n个订单,m个工厂,第i个订单在第j个工厂生产的时间为t[i][j],一个工厂能够生产多个订单,但一次仅仅能生产一个订单,也就是说假设先生产a订单,那么b订单要等到a生产完以后再生产,问n个订单用这m个工厂所有生产完须要最少的时间是多少。

    思路转载于:http://blog.csdn.net/lin375691011/article/details/19292473

    这个题在建图上有一些须要思考非常长时间的地方。由于每一个订单所消耗的时间是车间完毕订单的时间加上订单等待的时间。我们设在车间A须要完毕k个订单,消耗的总时间是t1+(t1+t2)+(t1+t2+t3)……转换一下就是t1*k+t2*(k-1)+t3*(k-3)……我们就找到了规律:当第i个订单在第j个车间是倒数第k个任务时,总消耗时间须要加上订单i在车间相应消耗时间的k倍。

    补充:也就是说把m个工厂看作m个点,每一个点又拆成n个点(由于每一个工厂最多能够生产n个订单),拆成的第i个点代表某一个订单在该工厂里面是倒数第i个被生产的。

    g[i][k*n+j]=-t[i][k]*(j+1);//第i个订单在第k个工厂生产,且在第k个工厂中生产的全部订单中它排在倒数第j位被生产

    0<=i<n ,   0<=k<m ,  0<=j<n

    如图:

    KM算法是求找出n条边所获得的最大权值,而在本题中要求最小权值,把所用时间取反,那么利用Km算法所求的最大权值的相反数就是我们所要求的最小权值。

    代码:

    #include <iostream>
    #include <string.h>
    #include <stdio.h>
    #include <algorithm>
    #include <iomanip>
    using namespace std;
    const int maxn=52;
    const int inf=0x3f3f3f3f;
    int n,m;
    int t[maxn][maxn];
    int nx,ny;
    int g[maxn][maxn*maxn];
    int linked[maxn*maxn],lx[maxn],ly[maxn*maxn];
    int slack[maxn*maxn];
    bool visx[maxn],visy[maxn*maxn];
    
    bool DFS(int x)
    {
        visx[x]=true;
        for(int y=0;y<ny;y++)
        {
            if(visy[y])
                continue;
            int tmp=lx[x]+ly[y]-g[x][y];
            if(tmp==0)
            {
                visy[y]=true;
                if(linked[y]==-1||DFS(linked[y]))
                {
                    linked[y]=x;
                    return true;
                }
            }
            else if(slack[y]>tmp)
                slack[y]=tmp;
        }
        return false;
    }
    
    int KM()
    {
        memset(linked,-1,sizeof(linked));
        memset(ly,0,sizeof(ly));
        for(int i=0;i<nx;i++)
        {
            lx[i]=-inf;
            for(int j=0;j<ny;j++)
                if(g[i][j]>lx[i])
                lx[i]=g[i][j];
        }
        for(int x=0;x<nx;x++)
        {
            for(int i=0;i<ny;i++)
                slack[i]=inf;
            while(true)
            {
                memset(visx,0,sizeof(visx));
                memset(visy,0,sizeof(visy));
                if(DFS(x))
                    break;
                int d=inf;
                for(int i=0;i<ny;i++)
                    if(!visy[i]&&d>slack[i])
                    d=slack[i];
                for(int i=0;i<nx;i++)
                    if(visx[i])
                    lx[i]-=d;
                for(int i=0;i<ny;i++)
                {
                    if(visy[i])
                        ly[i]+=d;
                    else
                        slack[i]-=d;
                }
            }
        }
        int ans=0;
        for(int i=0;i<ny;i++)
            if(linked[i]!=-1)
            ans+=g[linked[i]][i];
        return -ans;
    }
    
    int main()
    {
        int cas;
        cin>>cas;
        while(cas--)
        {
            cin>>n>>m;
            for(int i=0;i<n;i++)
                for(int j=0;j<m;j++)
                cin>>t[i][j];
            for(int i=0;i<n;i++)
                for(int j=0;j<n;j++)
                    for(int k=0;k<m;k++)
                        g[i][k*n+j]=-t[i][k]*(j+1);//第i个订单在第k个工厂生产,且在第k个工厂中生产的全部订单中它排在倒数第j位被生产
            m=n*m;
            nx=n;ny=m;
            cout<<setiosflags(ios::fixed)<<setprecision(6)<<1.0*KM()/n<<endl;
        }
        return 0;
    }
    


     

     

     

     

     

  • 相关阅读:
    python连接SMTP的TLS(587端口)发邮件python发邮件(带认证,587端口)202010
    JAVA抓取通过JS渲染的网站(动态)网页数据
    moviepy音视频剪辑:与大小相关的视频变换函数详解
    区块链知识博文1: 共识算法之争(PBFT,Raft,PoW,PoS,DPoS,Ripple)
    MoviePy v2.0.0.dev1尚不成熟,不建议大家使用
    区块链学习2:一些概念性的基础知识笔记
    老猿学5G:3GPP 5G规范中的URI资源概念
    区块链学习1:Merkle树(默克尔树)和Merkle根
    老猿Python博客文章目录索引
    老猿学5G:融合计费场景的Nchf_ConvergedCharging_Create、Update和Release融合计费消息交互过程
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4339665.html
Copyright © 2020-2023  润新知