【分数规划】

分数规划

==建议先看资料照着推一下 知道怎么回事了以后忘了也会推...

资料

这样一类问题,给定两个数组,benifit[i]表示选取i的收益,cost[i]表示选取i的代价。如果选取i,定义x[i]=1否则x[i]=0。每一个物品只有选或者不选两种方案,求一个选择方案使得\(R=\sum(benifit[i]*x[i])/\sum(cost[i]*x[i])\)取得最值,即所有选择物品的总收益与总代价的比值最大或是最小

我的笔记当然是在笔记本上鸭

实战

POJ2976Dropping tests 普通01分数规划

题意:给出n个a和b,让选出n-k个使得\((\sum a[i])/(\sum b[i])\)最大
普通分数规划 就是因为是实数要注意一些小细节

二分
#define ll long long
#define Abs(x) ((x)<0?-(x):(x))
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
const int N=1e5+5,M=5e5+5,INF=1e9+7,inf=0x3f3f3f3f;
const double eps=1e-7;
int n,k,a[N],b[N];
double d[N];
template void rd(t &x){
    x=0;int w=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=w?-x:x;
}
double check(double l){
	double sum=0.0;
	for(int i=1;i<=n;++i) d[i]=(double)a[i]-l*b[i];
	sort(d+1,d+n+1);
	for(int i=n-k+1;i<=n;++i) sum+=d[i];
	return sum;
}
int main(){
	freopen("bb.txt","r",stdin);
	rd(n),rd(k);
	double l=0.0,r=0.0,mid;
	for(int i=1;i<=n;++i) rd(a[i]);
	for(int i=1;i<=n;++i){
		rd(b[i]);
		r=Max(r,1.0*a[i]/b[i]);
	}
	while(r-l>=eps){
		mid=(l+r)/2;
		if(check(mid)>0) l=mid;
		else r=mid;
	}
	printf("%.4lf\n",l);
	return 0;
}
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring> 
using namespace std;
#define ll long long
#define Abs(x) ((x)<0?-(x):(x))
const int N=1000+5,M=5e5+5,INF=1e9+7,inf=0x3f3f3f3f;
const double eps=1e-7;
int n,k;
double a[N],b[N],d[N];
template <class t>void rd(t &x){
    x=0;int w=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=w?-x:x;
}

double check(double l){
	double sum=0.0;
	for(int i=1;i<=n;++i) d[i]=a[i]-l*b[i];
	sort(d+1,d+n+1);
	for(int i=k+1;i<=n;++i) sum+=d[i];
	return sum;
}

int main(){
	while(scanf("%d%d",&n,&k)!=EOF&&n+k){
		double l=0.0,r=0.0,mid;
		for(int i=1;i<=n;++i) scanf("%lf",&a[i]);
		for(int i=1;i<=n;++i){
			scanf("%lf",&b[i]);
			if(a[i]/b[i]>r) r=a[i]/b[i];
		}
		while(r-l>eps){
			mid=(l+r)/2;
			if(check(mid)>0) l=mid;
			else r=mid;
		}
		printf("%.0f\n",l*100);
	}
	return 0;
}

POJ2728Desert King 最优比率生成树

有n个点,每个点有一个坐标和高度
两点之间的费用是高度之差的绝对值 两点之间的距离就是欧几里得距离
求一棵生成数,使得单位距离的费用最小

即求得花费/收益比值最小

(开始没懂...原来是没好好读题)

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
#define Abs(x) ((x)<0?-(x):(x))
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
const int N=1000+5,M=5e5+5,INF=1e9+7,inf=0x3f3f3f3f;
const double eps=1e-7;
int n,k;
double a[N][N],b[N][N];
template <class t>void rd(t &x){
    x=0;int w=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=w?-x:x;
}

struct node{int x,y,h;}nd[N];
double qdis(int i,int j){return sqrt(1.0*(nd[i].x-nd[j].x)*(nd[i].x-nd[j].x)+1.0*(nd[i].y-nd[j].y)*(nd[i].y-nd[j].y));}

bool vis[N];
double dis[N],d[N][N];
double prim(double x){
	double sum=0.0;
	for(int i=0;i<=n;++i) dis[i]=1e20,d[i][i]=0.0,vis[i]=0;
	dis[1]=0.0;
	for(int i=1;i<=n;++i)
	for(int j=i+1;j<=n;++j)
	d[i][j]=d[j][i]=b[i][j]-x*a[i][j];
	for(int i=1,u=0;i<=n;++i,u=0){
		for(int j=1;j<=n;++j)
		if(!vis[j]&&dis[j]<dis[u]) u=j;
		vis[u]=1,sum+=dis[u];
		for(int v=1;v<=n;++v)
		if(!vis[v]) dis[v]=Min(dis[v],d[u][v]);
	}
	return sum;
}

int main(){
	freopen("in.txt","r",stdin);
	while(scanf("%d",&n)!=EOF&&n){
		double l=0.0,r=0.0,mid;
		for(int i=1;i<=n;++i) rd(nd[i].x),rd(nd[i].y),rd(nd[i].h);
		for(int i=1;i<=n;++i)
		for(int j=i+1;j<=n;++j)
		a[i][j]=a[j][i]=qdis(i,j),b[i][j]=b[j][i]=1.0*Abs(nd[i].h-nd[j].h),r=Max(r,b[i][j]/a[i][j]);
		while(r-l>=eps){
			mid=(l+r)/2;
			if(prim(mid)>0) l=mid;
			else r=mid;
		}
		printf("%.3f\n",l);
	}
	return 0;
}

USACOSightseeing Cows 最优比率环

在有向图图中选出一个环,使得这个环的点权/边权最大

即在每次check的时候找一个正环 可是正环并不好找 就将边权改为负值去找负环

QQ图片20190815165640.jpg打了一个下午超级悲伤 不知道哪里错了一直给我报错 然后怒而全部删掉重打 然后一遍过... 一言难尽

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
#define Abs(x) ((x)<0?-(x):(x))
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
const int N=1000+5,M=5000+5,INF=1e9+7,inf=0x3f3f3f3f;
const double eps=1e-7;
int n,m,a[N],fr[M],to[M],co[M];
template <class t>void rd(t &x){
    x=0;int w=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=w?-x:x;
}

int head[N],tot;
struct edge{int v,nxt;double w;}e[M];
void add(int u,int v,double w){
	e[++tot]=(edge){v,head[u],w},head[u]=tot;
}

bool vis[N];double dis[N];
int cnt[N];
queue<int>q;
bool spfa(){
	while(!q.empty()) q.pop();
	for(int i=1;i<=n;++i) dis[i]=0.0,cnt[i]=vis[i]=1,q.push(i);//图有可能不连通
	while(!q.empty()){
		int u=q.front();
		q.pop(),vis[u]=0;
		for(int i=head[u],v;i;i=e[i].nxt){
			v=e[i].v;
			if(dis[v]>dis[u]+e[i].w){
				dis[v]=dis[u]+e[i].w;
				if(!vis[v]) q.push(v),vis[v]=1,++cnt[v];
				if(cnt[v]>=n) return 1;
			}
		}
	}
	return 0;
}
bool check(double x){
	memset(head,0,sizeof(head)),tot=0;
	for(int i=1;i<=m;++i) add(fr[i],to[i],-((double)a[to[i]]-x*co[i]));
	if(spfa()) return 1;//找到负环
	else return 0;
}


int main(){
	freopen("in.txt","r",stdin);
	rd(n),rd(m);
	for(int i=1;i<=n;++i) rd(a[i]);
	for(int i=1;i<=m;++i) rd(fr[i]),rd(to[i]),rd(co[i]);
	double l=0.0,r=3000.0,mid;
	while(r-l>=eps){
		mid=(l+r)/2;
		if(check(mid)) l=mid;
		else r=mid;
	}
	printf("%.2f",l);
	return 0;
}

[USACO18OPEN]Talent Show 分数规划+背包

使总重量最少为W的奶牛可能达到的总才艺值与总重量的比值最大

01分数规划 然后跑背包来判断是否可行 初始化值时要赋为极小是lch讲的时候强调过的QAQ

我太瘟辽 该好好练一下动规 在看到重量最少为多少时想了好一会儿怎么转移

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
#define Abs(x) ((x)<0?-(x):(x))
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
const int N=300+5,M=1000+5,INF=1e9+7,inf=0x3f3f3f3f;
const double eps=1e-7;
int n,W,a[N],b[N];
template <class t>void rd(t &x){
    x=0;int w=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=w?-x:x;
}

double d[N],f[N][M];
bool check(double mid){
	for(int i=0;i<=n;++i){
		if(i) d[i]=(double)a[i]-mid*b[i];
		for(int j=0;j<=W;++j) f[i][j]=-INF;
	}
	f[0][0]=0.0;
	for(int i=1;i<=n;++i)
	for(int j=0;j<=W;++j)
	f[i][j]=Max(f[i][j],f[i-1][j]),
	f[i][Min(W,j+b[i])]=Max(f[i][Min(W,j+b[i])],f[i-1][j]+d[i]);
	return f[n][W]>0;
}


int main(){
	freopen("in.txt","r",stdin);
	rd(n),rd(W);
	double l=0.0,r=0.0,mid;
	for(int i=1;i<=n;++i) rd(b[i]),rd(a[i]),r=Max(r,(double)a[i]/b[i]);
	while(r-l>=eps){
		mid=(l+r)/2;
		if(check(mid)) l=mid;
		else r=mid;
	}
	printf("%d",(int)(l*1000));
	return 0;
}

==还有[SCOI2014]方伯伯运椰子 [SDOI2017]新生舞会 都是分数规划+网络流 懒得做了

posted @ 2019-08-15 17:00  委屈的咸鱼鱼鱼鱼  阅读(178)  评论(0编辑  收藏  举报