POJ3686 The Windy's 神奇的拆点

Description

The Windy's is a world famous toy factory that owns M top-class workshop to make toys. This year the manager receives N orders for toys. The manager knows that every order will take different amount of hours in different workshops. More precisely, the i-th order will take Zij hours if the toys are making in the j-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 matrix Zij (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.

题意:

玩具工厂生产玩具!有M个车间,有N个玩具要生产,给出一张N*M的图表示Mj车间生产玩具Ni所需要的时间,问平均每个玩具的完成时间是多少(一个车间一次只能生产一个玩具且不能中途放弃)?

解题思路:

假如一个车间要生产3个玩具分别耗时t1,t2,t3,那么总完成时间就是t1+(t1+t2)+(t1+t2+t3),变形一下就是3*t1+2*t2+3*t3,这就等价于倒数第i个进入车间的玩具,对该车间总耗时的贡献为i*ti(是等价)。然后我们就来拆点吧,一个车间最多生产n个玩具,就每个车间拆成n个点,每个点与相应玩具连接的权值为依次是1*tij,2*2ij,3*tij....n*tij,然后所有玩具和源点连接,所有拆出的点和汇点连接,跑一遍最小费用流。算出总耗时/n就ok啦。

代码:

#include <stdio.h>  
#include <string.h>  
const int Maxn = 55;  
const int Maxm = Maxn*Maxn;  
const int inf = 1<<30;  
int n,m,t[Maxn][Maxn],w[Maxm][Maxm],km1[Maxm],km2[Maxm],pre[Maxm],d;  
bool visx[Maxm],visy[Maxm];  
void init()  
{  
    memset(pre,-1,sizeof(pre));  
    memset(km1,0,sizeof(km1));  
    memset(km2,0,sizeof(km2));  
    for(int i=0; i<n; i++)  
    {  
        for(int j=0; j<m; j++)  
        {  
            scanf("%d",&t[i][j]);  
        }  
    }  
    for(int i= 0; i<n; i++)  
    {  
        for(int j = 0; j<m; j++)  
        {  
            for(int k=0; k<n; k++)  
            {  
                w[i][j+k*m]=-(t[i][j]*(k+1));  
            }  
        }  
    }  
    m*=n;  
}  
bool dfs(int src)  
{  
    visx[src]=true;  
    for(int i=0; i<m; i++)  
    {  
        if(!visy[i])  
        {  
            int t=km1[src]+km2[i]-w[src][i];  
            if(!t)  
            {  
                visy[i]=true;  
                if(pre[i]==-1||dfs(pre[i]))  
                {  
                    pre[i]=src;  
                    return true;  
                }  
            }  
            else if(d>t)d=t;  
        }  
    }  
    return false;  
}  
double KM()  
{  
    for(int i=0; i<n; i++)  
    {  
        while(1)  
        {  
            memset(visx,false,sizeof(visx));  
            memset(visy,false,sizeof(visy));  
            d=inf;  
            if(dfs(i))break;  
            for(int j=0;j<n;j++)  
            {  
                if(visx[j])  
                {  
                    km1[j]-=d;  
                }  
            }  
            for(int j=0;j<m;j++)  
            {  
                if(visy[j])  
                {  
                    km2[j]+=d;  
                }  
            }  
        }  
    }  
    double ans=0;  
    for (int i=0; i<m; i++)  
    {  
        if (pre[i]!=-1) ans+=w[pre[i]][i];  
    }  
    return -ans;  
}  
int main()  
{  
    int t;  
    scanf("%d",&t);  
    while(t--)  
    {  
        scanf("%d%d",&n,&m);  
        init();  
        printf("%.6lf\n",KM()/(n*1.0));  
    }  
    return 0;  
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值