poj 3686 The Windy's

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;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值