[SCOI2007] 修车

link

这道题的主要价值在于用到了一种重要的思想,即对费用提前计算。

假如这道题没有等待的时间,那么就是一个二分图最小匹配问题,用费用流跑一下即可。但它有等待时间,而每个人的等待时间和他前面的所有人是谁都有关系,这就造成了大大的困扰。于是考虑对费用提前计算,因为每个人使后来人等待的时间只和他后面的人数以及他自己是谁有关系,和他后面具体有哪些人没有关系。于是考虑建立点 \(a_{i,j}\) ,每个人连接它就代表了它寻找工人i而且是倒数第j个修的这样的决策。可以考虑这个决策会给总时间带去多少伤害,显然就是 \(c\times j\) ,正常连边跑最小费用最大流即可。

输入很讨厌,你不能把矩阵换个方向放置吗。

#include<bits/stdc++.h>
//#define feyn
const int N=70;
const int S=N*N*2;
const int M=3e6;
const int maxn=1e9;
using namespace std;
inline void read(int &wh){
	wh=0;int f=1;char w=getchar();
	while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
	while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
	wh*=f;return;
}

int m,n,a[N][N],c[N][N],b[N],ss,tt,cnt;

struct edge{
	int t,v1,v2,next;
}e[M];
int head[S],esum=1;
inline void adde(int fr,int to,int v1,int v2){
	e[++esum]=(edge){to,v1,v2,head[fr]};head[fr]=esum;
}
inline void add(int fr,int to,int v1,int v2){
	adde(fr,to,v1,v2);adde(to,fr,0,-v2);
}
queue<int>q;
int dis[S];
bool inq[S],vis[S];
inline bool check(){
	memset(dis,0x3f,sizeof(dis));
	memset(inq,0,sizeof(inq));
	memset(vis,0,sizeof(vis));
	q.push(ss);inq[ss]=true;dis[ss]=0;
	while(!q.empty()){
		int wh=q.front();q.pop();inq[wh]=false;
		for(int i=head[wh],th;i;i=e[i].next){
			if(e[i].v1==0)continue;
			int now=dis[wh]+e[i].v2;
			if(now>=dis[th=e[i].t])continue;
			dis[th]=now;if(inq[th]==false)inq[th]=true,q.push(th);
		}
	}
	return dis[tt]<=maxn;
}
int cost;
inline int dinic(int wh,int val){
	if(wh==tt)return cost+=val*dis[wh],val;
	int used=0;vis[wh]=true;
	for(int i=head[wh],th;i;i=e[i].next){
		if(e[i].v1==0||dis[th=e[i].t]!=dis[wh]+e[i].v2||vis[th])continue;
		int now=dinic(th,min(e[i].v1,val));
		if(now)used+=now,val-=now,e[i].v1-=now,e[i^1].v1+=now;
		if(val==0)break;
	}
	return used;
}

signed main(){
	
	#ifdef feyn
	freopen("in.txt","r",stdin);
	#endif
	
	ss=++cnt;tt=++cnt;
	read(m);read(n);int s1;
	for(int i=1;i<=n;i++)b[i]=++cnt,add(b[i],tt,1,0);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			read(c[j][i]);
		}
	}
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			a[i][j]=++cnt;add(ss,a[i][j],1,0);
			for(int k=1;k<=n;k++){
				add(a[i][j],b[k],1,c[i][k]*j);
			}
		}
	}
	int ans=0;
	while(check())dinic(ss,maxn);
	printf("%.2f",(double)cost/n);
	
	return 0;
}
posted @ 2022-07-14 16:31  Feyn618  阅读(22)  评论(0编辑  收藏  举报