http://poj.org/problem?id=3686
题意:有n个玩具和m台机器,给出每个玩具在每台机器上加工完成的时间,并且每台机器同一时间只能加工一个玩具,求加工完所有的玩具所需要的最少平均时间。
思路:
将n个玩具看做一个集合,m台机器看做一个集合,将每个玩具在每台机器上加工时间看做边,很容易想到二分图最佳匹配问题。
设N个任务的执行时间分别为T1,T2…TN,则N个订单的总的执行时间是
T1*N + T2*(N-1) + … + TN-1*2 + TN。
本题的关键是拆点构图。将每台机器拆成n个机器分点,将玩具i与机器j的第k个分点相连,容量为1,权值是map[i][j]*k,表示玩具i是机器j上倒数第k个完成的,完成该任务所消耗的时间是map[i][j]*k;注意是消耗的时间不是生产的时间,消耗的时间包括生产时间以及对后面k-1个玩具的延误时间。再附加一个超级源点与每个玩具相连,容量为1,权值为0;每个机器拆点后与超级汇点相连,容量为1,权值为0。构图完成。然后在其基础上求最小费用最大流。
这样构图后,总的顶点数为 n+n*m+2,总的边数是 (n+n*n*m+n*m)*2;乘2是因为加边是要加上反向边,容量为0,权值为其负值。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 2600;
const int INF = 0x3f3f3f3f;
int n,m,s,t,vn;
int map[55][55];
struct node
{
int u,v,w,c,next;
}edge[800000];
int p[maxn],cnt;
int pre[maxn],dis[maxn];
void add(int u, int v, int w, int c)
{
edge[cnt].u = u;
edge[cnt].v = v;
edge[cnt].w = w;
edge[cnt].c = c;
edge[cnt].next = p[u];
p[u] = cnt++;
edge[cnt].u = v;
edge[cnt].v = u;
edge[cnt].w = 0;
edge[cnt].c = -c;
edge[cnt].next = p[v];
p[v] = cnt++;
}
void init() //拆点构图
{
scanf("%d %d",&n,&m);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
scanf("%d",&map[i][j]);
cnt = 0;
memset(p,-1,sizeof(p));
s = 0; //超级源点
t = n+n*m+1;//超级汇点
vn = t+1; //总顶点数
for(int i = 1; i <= n; i++)
add(s,i,1,0);
for(int i = n+1; i <= n+n*m; i++)
add(i,t,1,0);
for(int i = 1; i <= n; i++)
{
int count = n+1;
for(int j = 1; j <= m; j++)
{
for(int k = 1; k <= n; k++)
add(i,count++,1,map[i][j]*k);
}
}
}
bool spfa() //spfa寻找增广路
{
queue<int>que;
while(!que.empty()) que.pop();
int inque[maxn];
memset(pre,-1,sizeof(pre));
memset(inque,0,sizeof(inque));
for(int i = 0; i <= vn; i++)
dis[i] = INF;
dis[s] = 0;
inque[s] = 1;
que.push(s);
while(!que.empty())
{
int u = que.front();
que.pop();
inque[u] = 0;
for(int i = p[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
int c = edge[i].c;
if(edge[i].w && dis[v] > dis[u]+c)
{
dis[v] = dis[u]+c;
pre[v] = i;
if(!inque[v])
{
inque[v] = 1;
que.push(v);
}
}
}
}
if(dis[t] < INF) return true;
else return false;
}
void solve()
{
int ans = 0;
while(spfa())
{
for(int i = pre[t]; i != -1; i = pre[edge[i].u])
{
edge[i].w--;
edge[i^1].w++;
}
ans += dis[t]; //更新总费用
}
double res = (ans*1.0)/n;
printf("%.6f\n",res);
}
int main()
{
int test;
scanf("%d",&test);
while(test--)
{
init();
solve();
}
return 0;
}