【luogu 2053】修车(费用流)
【题目大意】
n个需要维修的车,m个工人,每个工人所用时间不同,求顾客平均等待时间最小
【解题思路】
最开始想着,n个车,m个工人之间每个连一条边然后求然后发现自己好像根本没有了解题目意思,就开始瞎建图
我一直对网络流的理解,优雅的暴力,优雅的在相互矛盾的条件(如果选了这个就不能选另一个)下找到最优解
这个题关键是如何把整体的等待时间和安装顺序表现出来,而不会导致矛盾
那么如何体现安装顺序呢
将一个工人在第1,2,3...个安装这辆车的情况都枚举出来独立建点,这样的话如果这辆车要第一个修,第二个修。。的情况都能够得到体现
n 辆车分别建点,向超级源连边,流量1,费用0
将 m 个工人对 n 辆车的时间拆成 n 个点,向超级汇连边,流量1,费用0
对 n 辆车的情况,对 m*n 个点连边,每 n 个点表示一个工人将这个车第 k 个修理所对应的时间,第 i 个工人修理第 j 辆车需要 x 时间,则第 j 辆车向对应的 n 个点连边,流量1,费用依次为 k*x(k=1~n)
然后跑最小费用流
【代码】
// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int MAXN = 1000000;
const int INF = (1 << 31) - 1;
struct node
{
int to;
int nt;
int cost;
int cap;
int rev;
}edge[MAXN], tmp[MAXN];
int st[MAXN], top;
int n, m, v, dis[MAXN];
int pre[MAXN], pree[MAXN];
void add(int x, int y, int cost, int cap)
{
top++, edge[top] = { y,st[x],cost,cap,top + 1 }, st[x] = top;
top++; edge[top] = { x,st[y],-cost,0 ,top - 1 }, st[y] = top;
}
int flow(int sp, int ep, int f)
{
ll res = 0;
//printf("**\n");
while (f > 0)
{
//printf("1\n");
fill(dis, dis + 1 + v, INF);
dis[sp] = 0;
bool update = true;
while (update)
{
update = false;
for (int i = 0; i <= v; i++)
{
if (dis[i] == INF) continue;
for (int j = st[i]; j != -1; j = edge[j].nt)
{
int to = edge[j].to;
//if (edge[j].cap == 0) continue;
if (to == i) continue;
if (edge[j].cap > 0 && dis[to] > dis[i] + edge[j].cost)
{
dis[to] = dis[i] + edge[j].cost;
pre[to] = i;
pree[to] = j;
update = true;
}
}
}
}
// printf("2\n");
if (dis[ep] == INF)
return -1;
int d = f;
for (int i = ep; i != sp; i = pre[i])
{
d = min(d, edge[pree[i]].cap);
}
// printf("f= %d d = %d\n",f, d);
f -= d;
res += d * dis[ep];
for (int i = ep; i != sp; i = pre[i])
{
edge[pree[i]].cap -= d;
int rev = edge[pree[i]].rev;
edge[rev].cap += d;
}
//printf("3\n");
}
return res;
}
int main()
{
memset(st, -1, sizeof(st));
scanf("%d%d", &m, &n);
int sp = 0, ep = n * m + n + 1;
v = ep;
for (int i = 1; i <= n; i++) add(sp, i, 0, 1);
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
{
add(n + (i - 1)*n + j, ep, 0, 1);
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
int x;
scanf("%d", &x);
for (int k = 1; k <= n; k++)
{
add(i, n + (j - 1)*n + k, k*x, 1);
}
}
printf("%.2lf", (((double)flow(sp, ep, n)) / ((double)n)));
return 0;
}