4819: [Sdoi2017]新生舞会 分数规划

题目

https://www.lydsy.com/JudgeOnline/problem.php?id=4819

思路

分数规划的模板题?(好菜呀)
假如n=3吧(懒得写很长的式子)
\(c=\frac{a_1+a_2+a_3}{b_1+b_2+b_3}\)
我们先二分一下,变为判定性问题
c是否大于等于xxxx
\(c>=\frac{a_1+a_2+a_3}{b_1+b_2+b_3}\)
\((b_1+b_2+b_3)*c>=a_1+a_2+a_3\)
\(0>=(a_1-c*b_1)+(a_2-c*b_2)+(a_3-c*b_3)\)
取反跑费用流就好了
每次cnt没=1,居然T了
说:没油圈就可以取反跑费用流

代码

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 5e5 + 7,inf=0x3f3f3f3f;
const double eps=1e-7;
int read() {
    int x=0,f=1;char s=getchar();
    for (;s>'9'||s<'0';s=getchar()) if(s=='-') f=-1;
    for (;s>='0'&&s<='9';s=getchar()) x=x*10+s-'0';
    return x*f;
}
int n,S,T;
int a[120][119],b[110][120];
struct node {
	int u,v,nxt,cap;
	double cost;
}e[N];
int head[N],cnt=1;
void add_edge(int u,int v,int cap,double cost) {
	e[++cnt].v=v;
	e[cnt].u=u;
	e[cnt].cap=cap;
	e[cnt].cost=cost;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
void Add(int u,int v,int cap,double cost) {
	add_edge(u,v,cap,cost);
	add_edge(v,u,0,-cost);
}
double dis[1001];
int frm[1001];
bool vis[1001];
queue<int> q;
bool spfa() {
	for(int i=0;i<=n+n;++i) dis[i]=-inf;
	dis[T]=-inf;
	memset(vis,0,sizeof(vis));
	memset(frm,0,sizeof(frm));
	q.push(S);
	dis[S]=0;
	while(!q.empty()) {
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=e[i].nxt) {
			int v=e[i].v;
			if(e[i].cap&&dis[v]<dis[u]+e[i].cost) {
				dis[v]=dis[u]+e[i].cost;
				frm[v]=i;
				if(!vis[v]) vis[v]=1,q.push(v);
			}
		}
	}
	return dis[T]!=-inf;
}
double work() {
	double ans=0;
	while(spfa()) {
		int now_flow=inf;
		for(int i=frm[T];i;i=frm[e[i].u])
			now_flow=min(now_flow,e[i].cap);
		for(int i=frm[T];i;i=frm[e[i].u]) {
			e[i].cap-=now_flow;
			e[i^1].cap+=now_flow;
			ans+=now_flow*e[i].cost;
		}
	}
	return ans;
}
bool check(double c) {
	memset(e,0,sizeof(e));
	memset(head,0,sizeof(head));
	cnt=1;
	S=2*n+1,T=2*n+2;
	for(int i=1;i<=n;++i) Add(S,i,1,0),Add(i+n,T,1,0);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			Add(i,j+n,1,a[i][j]-c*b[i][j]);
	double tmp=work();
	return tmp>=eps;//精度高了,等不等号差不多了
}
int main() {
	//read
	n=read();
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			a[i][j]=read();
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			b[i][j]=read();
	double l=0,r=1e4+1,ans=0;
	while(r-l>=eps) {
		double mid=(l+r)/2;
		if(check(mid)) ans=mid,l=mid;
		else r=mid;
	}
	printf("%.6lf\n",ans);
    return 0;
}
posted @ 2019-02-16 11:29  ComplexPug  阅读(113)  评论(0编辑  收藏  举报