[USACO18OPEN] Talent Show

[USACO18OPEN]Talent Show && 18.09.21模拟赛T2 mynameiszbt

洛谷P4377 传送门

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 }
Talent Show && mynameiszbt

另外,01分数规划还可以应用到树上和图里。

网上有好多这样的题啊我都没做过。

posted @ 2018-09-22 09:19  cervusky  阅读(172)  评论(0编辑  收藏  举报

Contact with me