题目链接:http://poj.org/problem?id=3686
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.
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
有n个订单m个车间,每个车间每次只能单独完成任何一个订单。完成多个需在前面结束才能开始下一个,给出每个订单在某个车间完成所用的时间。问订单完成的平均时间是多少。
题目思路:建图不容易想到啊。
因为每个订单所消耗的时间是车间完成订单的时间加上订单等待的时间。我们设在车间A需要完成k个订单,消耗的总时间是t1+(t1+t2)+(t1+t2+t3)……转换一下就是t1*k+t2*(k-1)+t3*(k-3)……我们就找到了规律:当第i个订单在第j个车间是倒数第k个任务时,总消耗时间需要加上订单i在车间对应消耗时间的k倍。图就建好了,上KM板子,求最小匹配,边权取负,结果再取反就是最小了。
注意:输出不能用lf,用lf一只WA,为什么啊? == QAQ
代码:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>
using namespace std;
#define FOU(i,x,y) for(int i=x;i<=y;i++)
#define FOD(i,x,y) for(int i=x;i>=y;i--)
#define MEM(a,val) memset(a,val,sizeof(a))
#define PI acos(-1.0)
const double EXP = 1e-9;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const ll MINF = 0x3f3f3f3f3f3f3f3f;
const double DINF = 0xffffffffffff;
const int mod = 1e9+7;
const int N = 3000;
int cost[N][N];
int nx,ny; //两边的点数
int g[N][N]; //二分图描述,g[i][j]代表i到j之间的权值,最大匹配初始化为-INF表示无边,同理最小匹配初始化为INF
int linker[N]; //记录y链接的哪个x,-1代表无
int lx[N],ly[N];//y中各点匹配状态,x,y中的点标号
int slack[N]; //slack为修改量
bool visx[N],visy[N];
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(linker[y] == -1 || DFS(linker[y]))
{
linker[y] = x;
return true;
}
}
else if(slack[y] > tmp)
slack[y] = tmp;
}
return false;
}
int KM()
{
memset(linker,-1,sizeof(linker));
memset(ly,0,sizeof(ly)); //初始右边期望值都是0
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,false,sizeof(visx));
memset(visy,false,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 res = 0;
for(int i = 0;i < ny;i++)
if(linker[i] != -1)
res += g[linker[i]][i];
return res;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
std::ios::sync_with_stdio(false);
int T,n,m;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
scanf("%d",&cost[i][j]);
for(int i=0;i<n;i++)
{
int cnt=0;
for(int j=0;j<m;j++)
for(int k=1;k<=n;k++)
g[i][cnt++] = -cost[i][j]*k;
}
nx=n;
ny=n*m;
int ans = -KM();
printf("%.6f\n",1.0*ans/n);
}
return 0;
}