[bzoj1070][SCOI2007]修车

1383. [SCOI 2007] 修车

同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小。

说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间。

输入

第一行有两个数M,N,表示技术人员数与顾客数。

接下来n行,每行m个整数。第i+1行第j个数表示第j位技术人员维修第i辆车需要用的时间T。

输出

最小平均等待时间,答案精确到小数点后2位。

样例

repair.in

2 2

3 2

1 4

repair.out

1.50

数据范围:

(2<=M<=9,1<=N<=60), (1<=T<=1000)

思路:建立一个超级源点和超级汇点,然后在这两个点之间在建立两排点,第一排为顾客数,从源点相这一排点各引一条容量为1,费用为0的边;然后再建立一排点,非别表示第一个技术人员修1辆车,2辆车......第m个技术人员修n辆车。然后在它们中间连一条容量为1,费用为修这辆车的费用乘上这是需等待的人数(也就是修的第几个车--》其实修的第几辆车是按倒序排列的,修倒数第一辆车时,只需要一个人等,而修倒数第二辆车时,则需要两个人等);再从第二排点向汇点各连一条容量为1,流量为0的边,做一次最小费用流即可。
其实这个题也可以建立三排点,也就是将第二排拆成两排点,分别表示技术人员数和需要修的车的数目。虽然在这个题中两种方法都行,但是在一道和他很类似的题美食节中只能用三排的方法。
#include<iostream>
#include<cstdio>
#include<cstring>
#define M 210000000
using namespace std;
int map[610][610][3]={0},pre[610],l[100000]={0},h,t,n,m,dis[610];
bool f[610];
inline int SPFA(int x,int y){
	memset(f,1,sizeof(f));
	memset(dis,127,sizeof(dis));
	memset(pre,0,sizeof(pre));
	int i,j,u;
	pre[x]=x;h=t=1;l[h]=x;dis[x]=0;
	while(h<=t){
		u=l[h];f[u]=true;
		for(i=1;i<=y;++i){
			if(map[u][i][1]>0&&u!=i&&dis[i]>dis[u]+map[u][i][2]){
				dis[i]=dis[u]+map[u][i][2];
				pre[i]=u;
				if(f[i]){
					f[i]=false;
					t+=1;
					l[t]=i;
				}
			}
		}
		h+=1;
	}
	return dis[y]>M?0:dis[y];
}
inline int ISAP(int x,int y){
	int minn=y,i;
	for(i=y;i!=x;i=pre[i]){
		minn=min(minn,map[pre[i]][i][1]);
	}
	for(i=y;i!=x;i=pre[i]){
		map[pre[i]][i][1]-=minn;
		map[i][pre[i]][1]+=minn;
	}
	return minn;
}
int main()
{
	freopen("scoi2007_repair.in","r",stdin);
	freopen("scoi2007_repair.out","w",stdout);
	int i,j,x,k,minn=1,ans=0;
	float out;
	scanf("%d%d",&m,&n);
	for(i=1;i<=n;++i){
		for(j=1;j<=m;++j){
			scanf("%d",&x);
			for(k=1;k<=n;++k){
				map[i+1][1+n+(j-1)*n+k][2]=k*x;
				map[1+n+(j-1)*n+k][i+1][2]=-k*x;
				map[i+1][1+n+(j-1)*n+k][1]=1;
				map[1+n+(j-1)*n+k][n*(m+1)+2][1]=1;
			}
		}
		map[1][i+1][1]=1;
	}
	while(minn){
		minn=SPFA(1,n*(m+1)+2);
		if(minn) ans+=minn*ISAP(1,n*(m+1)+2);
	}
	out=(float)ans/n;
	printf("%.2f\n",out);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值