01 分数规划小结
01分数规划是用来解决这样的一类问题:
有一堆物品,每一个物品有一个收益ai,一个代价bi,我们要求一个方案使选择的∑ai/∑bi最大。
这一类问题一般都有固定套路:
我们令x=∑ai/∑bi,我们要最大化x。
稍微改变一下得到:∑(ai-bi*x)>=0。
即我们需满足这个条件下得到最大的x。
那么我们就可以直接二分答案,即二分这个x的值。
这就直接是一个判定性问题了。
看一道板子:bzoj 5281 Talent Show
这个就是按上述套路,二分答案,再跑一个背包判断一下,就好了。
#include <bits/stdc++.h> using namespace std; inline int gi () { int x=0, w=0; char ch=0; while (! (ch>='0' && ch<='9') ) { if (ch=='-') w=1; ch=getchar (); } while (ch>='0' && ch<='9') { x= (x<<3) + (x<<1) + (ch^48); ch=getchar (); } return w?-x:x; } const int N=251; int n,W,l,r=100000,Mid,wi[N],val[N]; long long f[100010]; bool Check (int Val) { memset (f, 0xcf, sizeof (f) ); f[0]=0; for (int i=1;i<=n;++i) for (int j=W;j>=0;--j) { int k=min (j+wi[i], W); f[k]=max (f[k], f[j]+val[i]- (long long) wi[i]*Val); } return f[W]>=0; } int main () { n=gi (), W=gi (); for (int i=1;i<=n;++i) { wi[i]=gi (), val[i]=gi (); val[i]*=1000; } while (l<r) { Mid= (l+r+1) >>1; if (Check (Mid) ) l=Mid; else r=Mid-1; } printf ("%d\n", l); return 0; }
另外一道板子:bzoj 4753 最佳团体
同理,二分答案+树形背包判断。
#include <bits/stdc++.h> using namespace std; inline int gi () { int x=0, w=0; char ch=0; while (! (ch>='0' && ch<='9') ) { if (ch=='-') w=1; ch=getchar (); } while (ch>='0' && ch<='9') { x= (x<<3) + (x<<1) + (ch^48); ch=getchar (); } return w?-x:x; } const int N=2510; const double Eps=1e-5; int n,K,tot,head[N],Size[N]; double l,r=20000,Mid,Val[N],Pay[N],f[N][N]; struct Edge { int next, now; }e[N]; inline void Make (int from, int to) { e[++tot].next=head[from]; head[from]=tot; e[tot].now=to; } void DP (int x) { Size[x]=1; f[x][0]=0; f[x][1]=Val[x]-Pay[x]*Mid; for (int i=head[x];i;i=e[i].next) { int y=e[i].now; DP (y); for (int i=Size[x];i>=1;--i) for (int j=Size[y];j>=0;--j) f[x][i+j]=max (f[x][i+j], f[x][i]+f[y][j]); Size[x]+=Size[y]; } } bool Check () { memset (f, 0xc2, sizeof (f) ); memset (Size, 0, sizeof (Size) ); DP (0); if (f[0][K+1]>0) return 1; return 0; } int main () { K=gi (), n=gi (); for (int i=1, Pre;i<=n;++i) { scanf ("%lf%lf", &Pay[i], &Val[i]); Pre=gi (); Make (Pre, i); } while (r-l>=Eps) { Mid= (l+r) /2.0; if (Check () ) l=Mid; else r=Mid; } printf ("%.3lf\n", l); return 0; }
可以发现(个人感觉huaji)01分数规划是和背包紧密相连的。。