[USACO18OPEN]Talent Show && 18.09.21模拟赛T2 mynameiszbt
01分数规划。
下面直接搬来出题人zhx大佬的题解,感觉写的比网上的都好。
设有n组数据a,b,对于每一组数据,都有选和不选两种状态(设状态为x,选则x=1,不选则x=0),现在欲求出所有选中的数据中,∑a/∑b的最大值。
接下来是一些数学问题:
设原式最大值为R,R=∑(ai·xi)/∑(bi·xi)
若设L为某一种不那么优的选法,则恒有R>=L
则:∑(ai·xi)/∑(bi·xi)>=L
于是:∑(ai·xi)>=∑(bi·xi)·L
那么:∑(ai·xi)-∑(bi·xi)·L>=0
所以:∑((ai-bi·L)·xi)>=0
所以我们构造一个函数f(L),f(L)=∑((ai-bi·L)·xi)
这时我们讨论一下函数f(L)的性质:
根本性质一:首先,对于所有有意义的R,L,f(L)>=0,
根本性质二:其次,设d=ai-bi·L,则d随L单调递减!
那么我们会发现,如果L足够大,那么无论x如何取值,所得函数值均小于0!
这就与根本性质一相矛盾。
于是我们发现,一定存在一个临界状态的L能够使得∃f(L)>=0,而使得∀f(L+1)<0!
同时我们发现,如果我们求出了L的最大值,那么这个最大值其实就等价于R的值
而且这个L满足单调性,故可以二分答案!
对于每个L,我们仅需按贪心地求出f(L)最大值,判断其正负即可。
当然,对于本题,需要dp出最值即可,利用01背包。
本人对于01分数规划的理解:
把要解决的问题转换为可二分答案的式子。
至于为什么要用背包:因为这道题有一个总质量不小于W的限制,所以用背包求解。
设f[i]为质量为i的时候,f(L)的最大值,最后判断f(L)是否合法(是否大于0)。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define ll long long 5 using namespace std; 6 7 int n,tot; 8 int w[255],t[255]; 9 ll f[1005]; 10 11 int check(int k) 12 { 13 for(int i=1;i<=tot;i++)f[i]=-0x3f3f3f3f; 14 f[0]=0; 15 for(int i=1;i<=n;i++) 16 { 17 for(int j=tot;j>=0;j--) 18 { 19 if(f[j]==-0x3f3f3f3f)continue; 20 int nw=min(tot,j+w[i]); 21 f[nw]=max(f[nw],f[j]+t[i]-(ll)w[i]*k); 22 } 23 } 24 return f[tot]>=0; 25 } 26 27 int main() 28 { 29 //freopen("mynameiszbt.in","r",stdin); 30 //freopen("mynamelszbt.out","w",stdout); 31 scanf("%d%d",&n,&tot); 32 for(int i=1;i<=n;i++) 33 scanf("%d%d",&w[i],&t[i]),t[i]*=1000; 34 int ans=0; 35 for(int i=20;i>=0;i--) 36 if(check(ans|(1<<i)))ans|=(1<<i); 37 printf("%d",ans); 38 //fclose(stdin); 39 //fclose(stdout); 40 return 0; 41 }
另外,01分数规划还可以应用到树上和图里。
网上有好多这样的题啊我都没做过。