「刷题」最佳团体

  一开始想了一个很沙雕思路,是dp嵌套,先线性dp处理出每个人的必须入队的捆绑人之和(战斗力和代价),然后接着线性dp处理出最优的解,但发现算法假的,因为这个东西没有无后效果性。因为可能一个分子分母都很大的情况下比值很大,而另外一种情况是分子分母很小比值一样很大但是比前面那个小,这样小的被舍掉了。可是现在有一个比较小的捆绑人,加入到之前那个分子小的得到的比值会比加入那个大的要大。这样算法死了。

  然后紧接着想到了01分数规划(我还没打过题呢),因为这个形式真的很想,上下分子分母,可以小数二分答案,精度卡的又不是很死,唯一的问题就是捆绑人问题,我不能把它们分开,但是不分开又会算重,所以用树上背包来验证即可。

  但是要注意的是并不是取最大值而是取装满了的最大值,就是说装的是负数也要装入背包。这样在背包的一开始赋初值的时候直接赋上这个节点的物品,相当于强制购买。然后因为2500的数据要跑子树归并,瞎写一下就好了。

 

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<vector>
 5 using namespace std;
 6 const int maxn=2505,INF=1e9;
 7 int n,K,mx,a[maxn],b[maxn],sz[maxn],r[maxn],d[maxn];
 8 double c[maxn],dp[maxn][maxn],tmp[maxn];
 9 vector<int> ch[maxn];
10 void dfs(int x)
11 {
12     for(int i=0;i<ch[x].size();i++) dfs(ch[x][i]);
13     sz[x]=1;dp[x][1]=c[x];
14     for(int i=0;i<ch[x].size();i++)
15     {
16         int t=ch[x][i];
17         for(int j=0;j<=K;j++) tmp[j]=-INF;
18         for(int j=0;j<=min(sz[x],K);j++)
19             for(int k=0;k<=min(sz[t],K-j);k++)
20                 tmp[j+k]=max(dp[x][j]+dp[t][k],tmp[j+k]);
21         sz[x]+=sz[t];
22         for(int j=0;j<=min(sz[x],K);j++) dp[x][j]=max(tmp[j],dp[x][j]);
23     }
24 }
25 bool check(double mid)
26 {
27     for(int i=0;i<=n;i++)
28     {
29         c[i]=1.0*a[i]-mid*b[i];
30         for(int j=0;j<=K;j++) dp[i][j]=-INF;
31     }
32     dfs(0);
33     return dp[0][K]>=0;
34 }
35 int main()
36 {
37     scanf("%d%d",&K,&n);K++;
38     for(int i=1;i<=n;i++)
39     {
40         scanf("%d%d%d",&b[i],&a[i],&r[i]);
41         mx+=a[i];
42         ch[r[i]].push_back(i);
43     }
44     double l=0,r=1e7;
45     while(r-l>1e-4)
46     {
47         double mid=(l+r)/2;
48         if(check(mid)) l=mid;
49         else r=mid;
50     }
51     printf("%.3lf\n",l);
52     return 0;
53 }
最佳团体
posted @ 2019-07-30 21:26  Lrefrain  阅读(188)  评论(0编辑  收藏  举报