P4377 [USACO18OPEN]Talent Show G

原题链接

考察:01分数规划+01背包

错误思路:

       二分求最大值,check函数里对 t[i] - mid*w[i]排序,从大到小选,只要和为sum就一直选,最后检测选择的W是否>=m

       这个思路错在不能从大到小选,因为存在t[i] - mid*w[i]很小,但是w[i]很大使原本从大到小的W>=m

思路:

        01分数规划二分是没错的,主要在于check函数.这里的牛是随意的排列组合,组合问题比起dfs,都可以考虑一下背包dp.

        这个01背包实际是前i头牛,重量至少是j的最大值.因为f[i][j]要尽可能的大,所以f[i][m]可以由所有能从f[i-1][j+w[i]] (j+w[i]>=m) 的状态推来.

        实际dp转移方程是

        f[i][m] = max(f[i-1][m],f[i-1][j+w[i])+t[i] - mid*w[i]       j+w[i]>=m  选第i个

        f[i][j+w[i]] = max(f[i-1][j]+c[i],f[i-1][j+w[i])                     j+w[i]<m  选第i个

       上面都是选第i个的dp转移

       但是注意,这样只有j+w[i]和m状态转移了,还有f[i][j]  = max(f[i-1][j],f[i][j]) 需要转移.

       但是如果我们压缩为一维,后面j转移就不需要.

 1 #include <iostream>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 typedef long long LL;
 6 const double eps = 1e-6;
 7 const int N = 260,M = 1010,INF = 0x3f3f3f3f;
 8 int n,m,w[N],t[N];
 9 double f[M],c[N];
10 bool check(double mid)
11 {
12     for(int j=1;j<=m;j++) f[j] = -INF;
13     for(int i=1;i<=n;i++) c[i] = t[i] - mid*w[i];
14     for(int i=1;i<=n;i++)
15       for(int j=m;j>=0;j--)
16         if(j+w[i]>=m)
17           f[m] = max(f[m],f[j]+c[i]);
18         else 
19           f[j+w[i]] = max(f[j]+c[i],f[j+w[i]]);
20     if(f[m]>=0) return 1;
21     return 0;
22 }
23 int main()
24 {
25     scanf("%d%d",&n,&m);
26     for(int i=1;i<=n;i++) scanf("%d%d",&w[i],&t[i]);
27     double l = 0,r = 1001;
28     while(r-l>=eps)
29     {
30         double mid = (l+r)/2;
31         if(check(mid)) l = mid;
32         else r = mid;
33     }
34     int ans = r*1000;
35     printf("%d\n",ans);
36     return 0;
37 }

 

posted @ 2021-05-06 16:37  acmloser  阅读(61)  评论(0编辑  收藏  举报