problem
luogu-P2053
solution
假设只有一个工作人员。修车顺序为 p1,p2,...,pnp_1,p_2,...,p_np1,p2,...,pn 是一个 nnn 的排列。
那么对于第 iii 个被修的车的等待时间应为 Tp1+...TpiT_{p_1}+...T_{p_i}Tp1+...Tpi。
总的等待时间则是 Tp1+(Tp1+Tp2)+...+(Tp1+...+Tpn)=Tp1∗n+Tp2∗(n−1)+...+TpnT_{p_1}+(T_{p_1}+T_{p_2})+...+(T_{p_1}+...+T_{p_n})=T_{p_1}*n+T_{p_2}*(n-1)+...+T_{p_n}Tp1+(Tp1+Tp2)+...+(Tp1+...+Tpn)=Tp1∗n+Tp2∗(n−1)+...+Tpn。
observation1:\text{observation1}:observation1: 所有车的等待时间就是所有车主的等待时间。
observation2:\text{observation2}:observation2: 越晚修的车对应的修车时间贡献越少。
从每辆车被等待的时间考虑,每个师傅一次修一辆车,我们按照车辆数将修车师傅拆成 n∗mn*mn∗m 个点。
车与源点连边,师傅与汇点连边,第 iii 辆车和在 kkk 时间段被 jjj 师傅修理连边,花费 Ti,j∗kT_{i,j}*kTi,j∗k 表示其修车时间对总时间的贡献。
跑一个最小费用流即可。
如果有兴趣更深入地研究一下此题,可以做 NOI的《美食节》一题 。
code
#include <bits/stdc++.h>
using namespace std;
#define maxn 1000
struct node { int to, nxt, flow, cost; }E[maxn * maxn];
int head[maxn], dis[maxn], lst[maxn], vis[maxn];
queue < int > q;
int m, n, s, t, cnt = -1;void addedge( int u, int v, int w, int c ) {E[++ cnt] = { v, head[u], w, c }, head[u] = cnt;E[++ cnt] = { u, head[v], 0,-c }, head[v] = cnt;
}bool SPFA() {memset( dis, 0x3f, sizeof( dis ) );memset( lst, -1, sizeof( lst ) );dis[s] = 0; q.push( s );while( ! q.empty() ) {int u = q.front(); q.pop(); vis[u] = 0;for( int i = head[u];~ i;i = E[i].nxt ) {int v = E[i].to;if( dis[v] > dis[u] + E[i].cost and E[i].flow ) {dis[v] = dis[u] + E[i].cost; lst[v] = i;if( ! vis[v] ) vis[v] = 1, q.push( v );}}}return ~ lst[t];
}int MCMF() {int ans = 0;while( SPFA() ) {int flow = 1e9;for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] ) flow = min( flow, E[i].flow );for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] ) {E[i ^ 1].flow += flow;E[i].flow -= flow;ans += E[i].cost * flow;}}return ans;
}int main() {memset( head, -1, sizeof( head ) );scanf( "%d %d", &m, &n );s = 0, t = n * (m + 1) + 1;for( int i = 1;i <= n;i ++ ) {for( int j = 1, ti;j <= m;j ++ ) {scanf( "%d", &ti );for( int k = 1;k <= n;k ++ ) //i车在k时间段被j修理addedge( i + n * m, (k - 1) * m + j, 1e9, ti * k );}}for( int i = 1;i <= n;i ++ ) addedge( s, i + n * m, 1, 0 );for( int i = 1;i <= n * m;i ++ ) addedge( i, t, 1, 0 );printf( "%.2f\n", MCMF() * 1.0 / n );return 0;
}