分数规划
这样一类问题,给定两个数组,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)(y)?(x):(y)) #define Min(x,y) ((x)
#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的时候找一个正环 可是正环并不好找 就将边权改为负值去找负环

#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; }