题目描述
同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小。
说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间。
输入格式
第一行有两个数M,N,表示技术人员数与顾客数。
接下来n行,每行m个整数。第i+1行第j个数表示第j位技术人员维修第i辆车需要用的时间T。
输出格式
最小平均等待时间,答案精确到小数点后2位。
输入输出样例
输入 #1
2 2
3 2
1 4
输出 #1
1.50
说明/提示
(2<=M<=9,1<=N<=60), (1<=T<=1000)
把m个师傅拆成mn个点,第(i,j)个点表示第i个师傅倒数第j个修车,再建n个点表示n辆车,mn个点都与第k辆车相连,花费是jc[k][i],流量为1。然后mn个点与源点相连,流量为1,花费为0,n辆车与汇点相连,流量为1,花费为0。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn = 200005;
const int INF = 2e9+1;
const int inf = 0x7f7f7f7f;
int n,m,cnt = 1;
int head[maxn];
int pre[maxn],cost[maxn],flow[maxn];
int c[105][105];
int maxflow,mincost;
bool vis[maxn];
struct node
{
int to,next,flow,w;
}l[maxn];
queue<int>q;
void add(int x,int y,int z,int w)
{
cnt++;
l[cnt].to = y;
l[cnt].flow = z;
l[cnt].w = w;
l[cnt].next = head[x];
head[x] = cnt;
cnt++;
l[cnt].to = x;
l[cnt].flow = 0;
l[cnt].w = -w;
l[cnt].next = head[y];
head[y] = cnt;
}
bool spfa(int s,int t)
{
memset(cost,0x7f,sizeof(cost));
memset(flow,0x7f,sizeof(flow));
memset(vis,false,sizeof(vis));
q.push(s);
vis[s] = 1,cost[s] = 0,pre[t] = -1;
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = false;
for(int i=head[u]; i; i=l[i].next)
{
int v = l[i].to;
if(l[i].flow && cost[v] > cost[u] + l[i].w)
{
cost[v] = cost[u] + l[i].w;
pre[v] = i;
flow[v] = min(flow[u],l[i].flow);
if(!vis[v])
{
vis[v] = true;
q.push(v);
}
}
}
}
return cost[t] != inf;
}
void MCMF(int s,int t)
{
while(spfa(s,t))
{
int u = t;
maxflow += flow[t];
mincost += flow[t]*cost[t];
while(u != s)
{
l[pre[u]].flow -= flow[t];
l[pre[u]^1].flow += flow[t];
u = l[pre[u]^1].to;
}
}
}
int main()
{
int s,t;
scanf("%d%d",&m,&n);
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
scanf("%d",&c[i][j]);//第i辆车由第j个人修的花费
}
}
s = 1,t = 2;
for(int i=1; i<=n*m; i++)
{
add(s,i+2,1,0);
}
for(int i=1; i<=m; i++)//第i个人第j个修车,修第k辆车
{
for(int j=1; j<=n; j++)
{
for(int k=1; k<=n; k++)
{
int now = (i-1)*n+j+2;
add(now,k+n*m+2,1,c[k][i]*j);
}
}
}
for(int i=1; i<=n; i++)
{
add(n*m+i+2,t,1,0);
}
MCMF(s,t);
printf("%.2f",(double)mincost/(double)n);
}
平面上有 N 个点,每个点可以向比它纵坐标小的点连边,要求连成一颗二叉树,使得边长总和最小。
建图:拆点,源点向每个点的入点连容量为2,费用为0的边,每个点的出点连容量为1,费用为0的边。对于边(u,v),u的入点连向v的出点,费用为距离,容量为1。跑MCMF,若最大流不等于n-1,则无法连成二叉树,否则最小费用即为答案。
有些题目中有某个节点必须经过几次,或是某个东西必须要选的限制。通常通过满流或是带下界的网络流来控制下界,拆点限流来控制上界。
当某个点最多只能和k个点相连时,拆点时令源点向该点连一条容量为k的边,当某个点最多只能被访问k次时,令出点连一条容量为k的点到汇点。