普通01分数规划
由n个物品,每个物品由两个属性a和b,选出k个物品,使Σai / Σbi 的值最大
设x=Σai / Σbi, F()=Σa-xΣb, D()=a-xb
当F()=0时,则为答案;当F()>0时,则说明答案小了;当F()<0时,则说明答案大了
二分答案
double l=0,r=1.0,mid; while(r-l>1e-5) { mid=(l+r)/2; if(work(mid)) l=mid; else r=mid; } int work(double x) { for(int i=0;i<n;i++) d[i]=a[i]-x*b[i]; sort(d,d+n); double F=0; for(int i=n-1;i>=n-k;i--) F+=d[i]; return F>=0; }
最优比率生成树
带权无向图G,对于每条边有vali,costi。现在求一棵生成树T,最大(小)化∑val / ∑cost。
二分答案,对边赋值w=val-r*cost,有选前|G|-1条大的w,即求最大生成树,套用01分数规划的模板。
例题:http://poj.org/problem?id=2728
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> using namespace std; #define maxn 1050 #define inf 0x7fffffff static double eps=1e-4; int vis[maxn],x[maxn],y[maxn],z[maxn],p[maxn]; double d[maxn],cost[maxn][maxn],dis[maxn][maxn]; int n; double prim(double x) { double totcost=0,totdis=0; double sum=0.0; for(int i=1;i<=n;i++) p[i]=1; d[1]=0; memset(vis,0,sizeof(vis)); vis[1]=1; for(int i=2;i<=n;i++) d[i]=cost[1][i]-dis[1][i]*x; int k; for(int i=2;i<=n;i++) { double mincost=inf; for(int j=2;j<=n;j++) { if(!vis[j] && d[j]<mincost) { mincost=d[j]; k=j; } } vis[k]=1; sum+=mincost; totcost+=cost[p[k]][k]; totdis+=dis[p[k]][k]; for(int j=1;j<=n;j++) { if(!vis[j] && d[j]>cost[k][j]-dis[k][j]*x) { d[j]=cost[k][j]-dis[k][j]*x; p[j]=k; } } } // return totcost/totdis; ///二分 return sum; ///迭代 } int main( ) { while(scanf("%d",&n),n) { for(int i=1;i<=n;i++) { scanf("%d%d%d",&x[i],&y[i],&z[i]); for(int j=1;j<i;j++) { double tmp=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]); cost[i][j]=cost[j][i]=abs(z[i]-z[j]); dis[i][j]=dis[j][i]=sqrt(tmp); } } double a=0; // while(1) { // double b=prim(a); // if(abs(a-b)<eps) { // printf("%.3f ", a); // break; // } // else a=b; // } ///迭代 double head=0,tail=100000.0; while(tail-head>1e-5) { double mid=(head+tail)/2.0; a=prim(mid); if(a>=0) head=mid; else tail=mid; } printf("%.3f ", tail); } return 0; }
最优比率生成环
给定有点权和边权的图,求一个环,使得环的点权和与边权和的比值最大。
01分数规划+树形背包