【最小费用最大流】[SCOI2007]修车 BZOJ 1070

[SCOI2007]修车 BZOJ 1070

Time Limit: 1 Sec  Memory Limit:162 MB

Description

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

Input

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

Output

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

Sample Input

2 2
3 2
1 4

Sample Output

1.50

HINT

数据范围:
(2<=M<=9,1<=N<=60), (1<=T<=1000)






拿到题目,看起来很好写,根据读入给每个技术人员和顾客之间连边(流量为1,费用为读入的时间),再加入源和汇

然后跑一次费用流,得出来的答案要除以顾客人数,然后样例很容易就过了,在本机一测,很不幸的WA完了。。。。。


回过头来看题,仔细分析了一下,发现,刚才的程序中,我们没有累加上仍在等待的顾客的时间【如两个技术人员,三个顾客,那么必定有一个顾客会多等一会儿,这一段时间我们没有算进来】

前思后想不知道怎么累加,因为网络流有退流的思想,而与此同时我们不能很好的维护出这个多余的时间,So how to do it ?


我们可以换一个思路,想想以前做的网络流,如方格取数等等,都是拆点的(虽然这一体的拆点和那一题没有一点思想上的关联。。。。)


我们把每个技术人员分别拆成N个点,第K个点表示当前技术人员在修好前K-1个后来修第K个,所以这条边的费用应该是K*w(w为读入的时间)【应该能明白吧~】


这样,本题就解决了


如果你在BZOJ上提交,你要注意了,1s是总时限,由于你每次spfa需要memset,所以,你要memset的数组千万不要太大,否则会超时的。。。。。(坑死我了。。。)



测评情况(BZOJ)



C++ AC Code

/*http://blog.csdn.net/jiangzh7
By Jiangzh*/
#include<cstdio>
#include<cstring>
#include<queue>
using std::queue;
const int N=100;
const int M=100*100*10;
const int inf=0x3f3f3f3f;
int worker,costomer,map[N][N];
struct EG{int from,to,flow,cost,next;}edge[M];
int head[M],L,S,T;
int dist[M],inQ[M],pre[M];
queue<int> Q;

void add_edge(int a,int b,int c,int d)
{
	edge[L]=(EG){a,b,c,+d,head[a]};
	head[a]=L++;
	edge[L]=(EG){b,a,0,-d,head[b]};
	head[b]=L++;
}

void read()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&worker,&costomer);
	for(int i=1;i<=costomer;i++)
		for(int j=1;j<=worker;j++)
			scanf("%d",&map[j][i]);
	int cnt=costomer;
	for(int i=1;i<=worker;i++)
		for(int k=1;k<=costomer;k++)
		{
			++cnt;
			for(int j=1;j<=costomer;j++)
				add_edge(cnt,j,1,k*map[i][j]);
		}
	S=0;T=cnt+1;
	for(int i=costomer+1;i<=cnt;i++)
		add_edge(S,i,1,0);
	for(int i=1;i<=costomer;i++)
		add_edge(i,T,1,0);
}

bool spfa()
{
	memset(dist,0x3f,sizeof(dist));
	memset(inQ,0,sizeof(inQ));
	dist[S]=0; Q.push(S);
	while(!Q.empty())
	{
		int x=Q.front();
		Q.pop(); inQ[x]=0;
		for(int i=head[x];i!=-1;i=edge[i].next)
			if(edge[i].flow>0 && dist[edge[i].to]>dist[x]+edge[i].cost)
			{
				pre[edge[i].to]=i;
				dist[edge[i].to]=dist[x]+edge[i].cost;
				if(!inQ[edge[i].to])
				{
					inQ[edge[i].to]=1;
					Q.push(edge[i].to);
				}
			}
	}
	return dist[T]!=inf;
}

void work()
{
	int mincost=0;
	while(spfa())
	{
		int res=inf;
		for(int i=T;i!=S;i=edge[pre[i]].from)
			res=std::min(res,edge[pre[i]].flow);
		for(int i=T;i!=S;i=edge[pre[i]].from)
		{
			edge[pre[i]].flow-=res;
			edge[pre[i]^1].flow+=res;
		}
		mincost+=dist[T]*res;
	}
	//printf("%d\n",mincost);
	printf("%.2lf\n",(double)mincost/costomer);
}

int main()
{
	freopen("repair.in","r",stdin);
	freopen("repair.out","w",stdout);
	read();
	work();
	return 0;
}








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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值