【题意】:Jam不好好学习,然后就去帮别人修电脑了,在一家店里,有M个店员,现在有N个顾客,给出每个顾客对应给每个店员的修电脑的时间为Tij,问所有顾客要等待的最少时间。当然,一个顾客在某个店员那里完成之后,那个店员才会执行下一个顾客的任务
【分析】由于每个店员同时只能接待一个顾客, 而顾客数量N只有20,所以可以把每个店员拆点,拆成N个点P[i,j,k],表示j号店员倒数第k个接待顾客i。而对于每个店员倒数第一个顾客的接待时间是不会影响任何人了,所以因为他产生的时间为1*T[i][j],而倒数第二个接待的时间会影响倒数第一人,而总时间为倒数第一个人自己消耗的时间1*T[i][j]+倒数第二个人消耗的时间以及影响倒数第一个人的时间和2*T[i][j],这样倒数第n个增加的时间为n*T[i][j]。
这样就把M个店员拆成M*N个点,然后虚拟源点0向N个顾客各连一条费用0流量1的边(限制总流量为N),然后每个顾客连向每个店员在倒数第K个时的费用为K*T[i][j]流量为1的边。最后每个店员拆完点后的N*M个点连向虚拟汇点流量1费用0的边。这样求最小费用流就是所有顾客都完成后的最小时间花费。
【AC CODE】93ms
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f, MAXN = 400+20+10, MAXM = 20*20*20+20+20*20+10;
struct ZKW_MinCostMaxFlow
{
private:
int st, ed, ecnt, n;
int head[MAXN],cap[MAXM<<1],cost[MAXM<<1],to[MAXM<<1],nxt[MAXM<<1];//有反向边,记得两倍
int dis[MAXN];
void SPFA()
{
repe(i,0,n) dis[i] = INF;
priority_queue<pair<int, int> > q;
dis[st] = 0;
q.push(make_pair(0, st));
while(!q.empty()){
int u = q.top().second, d = -q.top().first;
q.pop();
if(dis[u] != d) continue;
for(int p = head[u]; p!=-1; p = nxt[p]){
int &v = to[p];
if(cap[p] && dis[v] > d + cost[p]){
dis[v] = d + cost[p];
q.push(make_pair(-dis[v], v));
}
}
}
repe(i,0,n) dis[i] = dis[ed] - dis[i];
}
int minCost, maxFlow;
bool use[MAXN];
int add_flow(int u, int flow)
{
if(u == ed)
{
maxFlow += flow;
minCost += dis[st] * flow;
return flow;
}
use[u] = true;
int now = flow;
for(int p = head[u]; p!=-1; p = nxt[p])
{
int &v = to[p];
if(cap[p] && !use[v] && dis[u] == dis[v] + cost[p])
{
int tmp = add_flow(v, min(now, cap[p]));
cap[p] -= tmp;
cap[p^1] += tmp;
now -= tmp;
if(!now) break;
}
}
return flow - now;
}
bool modify_label()
{
int d = INF;
repe(u,0,n) if(use[u])
for(int p = head[u]; p!=-1; p = nxt[p])
{
int &v = to[p];
if(cap[p] && !use[v]) d = min(d, dis[v] + cost[p] - dis[u]);
}
if(d == INF) return false;
repe(i,0,n) if(use[i]) dis[i] += d;
return true;
}
public:
void init()
{
clc(head,-1);
ecnt = 2;
}
void add_edge(int u, int v, int cc, int ww)//点u-v,cc-容量,ww-单位流量费用
{
cap[ecnt] = cc; cost[ecnt] = ww; to[ecnt] = v;
nxt[ecnt] = head[u]; head[u] = ecnt++;
cap[ecnt] = 0; cost[ecnt] = -ww; to[ecnt] = u;
nxt[ecnt] = head[v]; head[v] = ecnt++;
}
int zkw(int ss, int tt, int nn, int &flow)//ss-源点,tt-汇点,nn-点的总数(编号0~n)
{
st = ss, ed = tt, n = nn;
minCost = maxFlow = 0;
SPFA();
while(true)
{
while(true)
{
repe(i,0,n) use[i] = 0;
if(!add_flow(st, INF)) break;
}
if(!modify_label()) break;
}
flow = maxFlow;
return minCost;
}
}zkwflow;
int cost[30][30];
int main()
{
#ifdef SHY
freopen("d:\\1.txt", "r", stdin);
#endif
int t;
scanf("%d", &t);
while(t--)
{
int n,m;
scanf("%d%d", &m,&n);
repe(i,1,n)
{
repe(j,1,m)
{
scanf("%d", &cost[i][j]);
}
}
zkwflow.init();
repe(i,1,n)
{
zkwflow.add_edge(0,i,1,0);
repe(j,1,m)
{
repe(k,1,n)
{
zkwflow.add_edge(i,(j-1)*n+k+n,1,k*cost[i][j]);
}
}
}
int ed = (m-1)*n+n+n+1;
repe(j,1,m)
{
repe(k,1,n)
{
zkwflow.add_edge((j-1)*n+k+n,ed,1,0);
}
}
int flow;
printf("%d\n", zkwflow.zkw(0,ed,2+n+m*n,flow));
}
return 0;
}