题解 P3705 [SDOI2017]新生舞会


分析

做了几道分数规划题,选一些来写题解。

首先观察答案形式,就是求最终 \((\dfrac{\sum a_i'}{\sum b_i'})\geqslant ans\) 最大的 \(ans\),在转化一下形式,就是 \(\sum a_i'-ans\times b_i'\geqslant 0\)

所以我们去二分答案(显然越小的答案最终越容易使上面式子满足),然后每一组对答案的贡献就能计算出,然后又要求每一个人都要找到各自舞伴,式子中的大于等于可以视为使左边的值最大,所以在 check 函数中我们男女生建点,每一组的贡献用作建边,男生向女生建边,流量为 \(1\),费用 \(a[i][j]-mid\times b[i][j]\),超级源点向男生,女生向超级汇点建流量 \(1\),费用 \(0\) 的边,验证最大费用是否大于等于 \(0\) 即可。

代码

#include
using namespace std;
inline void read(int &res){
	res=0;
	int f=1;
	char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')res=(res<<1)+(res<<3)+c-48,c=getchar();
	res*=f;
}
int n,s,t;
int a[105][105];
int b[105][105];
int head[210],tot;
struct edge{
	int to,nex,w;
	double bq;
}e[100000];
inline void add(int qq,int mm,int w,double bq){
	e[++tot].to=mm;
	e[tot].nex=head[qq];
	e[tot].w=w;
	e[tot].bq=bq;
	head[qq]=tot;
}
double dis[210];
int pre[210],incf[210];
queueq;
int vis[210];
bool spfa(){
	for(int i=1;i<=t;i++){
		dis[i]=-1e18;
		incf[i]=1e9;
	}
	q.push(s);
	while(q.size()){
		int x=q.front();q.pop();
		vis[x]=0;
		for(int i=head[x];i;i=e[i].nex){
			if(!e[i].w)continue;
			int v=e[i].to;
			if(dis[v]=0;
}
int main()
{
	read(n);
	s=0;t=2*n+1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++)read(a[i][j]);
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++)read(b[i][j]);
	}
	double l=0,r=10000;
	while(r-l>=1e-7){
		double mid=(l+r)/2.0;
		if(check(mid))l=mid+1e-8;
		else r=mid-1e-8;
	}
	printf("%.6lf",l);
}