题目大概意思为有N个玩具,M个工厂,每个工厂一次只能生产一个玩具,顺序任意,求所有玩具的加工完的平均时间最小值
例:N = 3 M = 2 Z = {{1,100},{100,1},{1,100}}
就相当于第一个在一工厂加工完花了 1 时间,第二个在二工厂加工完花了 1 时间,第三个在一工厂要等第一个做完再做,花了 2 时间,所以总共花了 4 时间,平均花了1.333333
假设一个工厂里有num个玩具来加工,则在该工厂所花的时间为 T = num * t_1 + (num - 1) * t_2 +…+1 * t_num(t_第几个制作)
我们把一个工厂拆开来看,把 M 个拆成 M * N 个(因为 num <= N ), i * N + j 个工厂,相当于第 i 个工厂中第 num - j + 1 个制作玩具,即该工厂倒数第 j 个制作玩具
然后我们设置一个超级源点,连接 N 个玩具,边的容量为1,费用为0,这样便限制了一个玩具只能制造一次
再设置一个超级汇点,连接 N * M 个工厂,边的容量为1,费用为0,这样限制了一个工厂不会同时生产两个玩具
再把 N 个玩具和 N * M 个工厂分别相连( i * N + j ), 边的容量为0,费用为 制造时间 * j,这样当最大流为N时,M个工厂都能正确的得到生产时间,也就能正确得到生产总时间
(需要注意,由于我们求的时最小费用流,所以当 j 存在较小的没有使用时,是不会使用较大的,因为这样的 制造时间 * j 更小,所以这样可以导致我们的一个工厂拆开的 j 个工厂,相当于 j 从小到大被连接,这样相当于把 T = num * t_1 + (num - 1) * t_2 +…+1 * t_num 倒过来考虑了,是符合题意的)
套最小费用流模板,把最后得到的值除以 N 即可。
#include <iostream>
#include <stdio.h>
#include <vector>
#define INF 1000000005
using namespace std;
int n, m;
int data[55][55];
struct edge
{
int to, cap, coust, rev;
edge(int t, int c, int s, int r)
{
to = t; cap = c; coust = s; rev = r;
}
};
vector<struct edge> ddd[3005];
int dist[3005];
int prev[3005];
int prep[3005];
void add_edge(int from, int to, int cap, int coust)
{
ddd[from].push_back(edge(to, cap, coust, ddd[to].size()));
ddd[to].push_back(edge(from, 0, -coust, ddd[from].size() - 1));
}
int min_coust(int from, int to, int flow)
{
int res = 0;
while(flow > 0)
{
fill(dist, dist + n + n * m + 2, INF);
dist[from] = 0;
bool flag = true;
while(flag)
{
flag = false;
for(int i = 0; i < n + n * m + 2; i++)
{
if(dist[i] != INF)
{
for(int j = 0; j < ddd[i].size(); j++)
{
struct edge e = ddd[i][j];
if(e.cap != 0 && dist[i] + e.coust < dist[e.to])
{
flag = true;
dist[e.to] = dist[i] + e.coust;
prev[e.to] = i;
prep[e.to] = j;
}
}
}
}
}
int d = flow;
for(int i = to; i != from; i = prev[i])
{
d = min(d, ddd[prev[i]][prep[i]].cap);
}
for(int i = to; i != from; i = prev[i])
{
ddd[prev[i]][prep[i]].cap -= d;
ddd[i][ddd[prev[i]][prep[i]].rev].cap += d;
}
res = res + dist[to] * d;
flow = flow - d;
}
return res;
}
int main()
{
int N;
scanf("%d", &N);
while(N--)
{
for(int i = 0; i < n + n * m + 2; i++)
{
ddd[i].clear();
}
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
scanf("%d", &data[i][j]);
}
}
for(int i = 1; i <= n; i++)
{
add_edge(0, i, 1, 0);
for(int j = 1; j <= m; j++)
{
for(int k = 1; k <= n; k++)
{
add_edge(i, n + (j - 1) * n + k, 1, k * data[i][j]);
}
}
}
for(int i = 1; i <= m; i++)
{
for(int j = 1; j <= n; j++)
{
add_edge(n + (i - 1) * n + j, n + m * n + 1, 1, 0);
}
}
printf("%.6f\n", min_coust(0, n + m * n + 1, n) / (double)n);
}
}