CF994F Compute Power

题意

有一堆任务,每个计算机可以跑一或两个。第 i i i个任务需要消耗 a i a_i ai的能量,使用 b i b_i bi个核。你可以安排这些任务,安排在一个计算机上第二轮的任务的能量消耗必须比第一轮的小。问第一轮任务的能量总和除以核总数最小。

思路

看到能量除以总和最小,就想到01分数规划的问题。二分答案,对于这个答案跑DP检验是否可行。 a i ÷ b i ≤ x a_i\div b_i \le x ai÷bix 等价于 a i − b i ∗ x ≤ 0 a_i-b_i*x\le 0 aibix0 于是可以判断DP结果是否有小于0的状态来二分。
至于DP,因为放在第二轮的能量消耗必须要比第一轮的小,且第二轮可以不放,于是可以用 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示从大到小安排,安排到第 i i i个时,有 j j j个任务能量比 a i a_i ai大安排在了第一轮且还未被安排第二轮, k k k个任务能量跟 a i a_i ai相等安排在了第一轮且还未被安排第二轮的状态。

换句话说, f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]就是扫到 i i i,第二轮还可以安排 j j j个,有 k k k个跟 a i a_i ai相等的被安排在了第一轮时 ∑ i = 1 i ∈ { 第 一 轮 } a i − x ∗ ∑ i = 1 i ∈ { 第 一 轮 } b i \sum^{i\in\{第一轮\}}_{i=1}a_i-x*\sum^{i\in\{第一轮\}}_{i=1}b_i i=1i{}aixi=1i{}bi的最小值。

转移就是

if(a[i]<a[i-1]){//1
	f[i][j][0]=min(f[ i ][ j ][0],j-k+1>=0?f[i-1][j-k+1][k]:inf);
	f[i][j][1]=min(f[ i ][ j ][1],j-k>=0?f[i-1][j-k][k]+task[i].a-x*task[i].b:inf);
}else{//2
	f[i][j][k]=min(f[i-1][j+1][k],k-1>=0?f[i-1][j][k-1]+task[i].a-x*task[i].b:5e9);
}

意思就是:

  1. 如果 a i &lt; a i − 1 a_i&lt;a_{i-1} ai<ai1 ,那么跟 a i a_i ai相等且放在第一轮的状态只有 0 0 0 1 1 1,即 k ∈ { 0 , 1 } k\in\{0,1\} k{0,1},那么转移也就只有两种状态:
    1. i i i项任务安排在第二轮,此时第三维无疑是 0 0 0,那么 k k k就变为了枚举 i − 1 i-1 i1时有多少个任务与 a i − 1 a_{i-1} ai1相等且安排在了第一轮。这些任务明显大于 a i a_i ai,于是就用上一状态加上任务 i i i放在第二轮的贡献(无)更新答案。
    2. i i i项任务安排在第一轮,此时第三维是 1 1 1,那么 k k k也照样是枚举 i − 1 i-1 i1时有多少个任务与 a i − 1 a_{i-1} ai1相等且被安排在了第一轮。这些任务也是大于 k k k的,于是也是用上一轮的状态加上任务 i i i放在第一轮的贡献( a i − x ∗ b i a_i-x*b_i aixbi)更新答案。
    3. tips:
      1. 上一轮状态:因为枚举到的 k k k个任务比 a i a_i ai大了,所以现在枚举到的 j j j要减掉 k k k,然后再看当前(也就是第 i i i项)任务放第一轮还是第二轮.
      2. inf:把不可行的状态置成inf,使他不会更新之后的答案.
  2. 如果 a i = = a i − 1 a_i==a_{i-1} ai==ai1 ,那么便枚举当前 j j j和当前 k k k然后用当前任务放第一轮和第二轮的两种方案更新答案。

枚举 k k k的时候可以加点优化,但现在已经足够了。
然后最后扫一遍 f [ n ] [ j ] [ k ] f[n][j][k] f[n][j][k]看是不是有状态小于0;
有就可以达到更小的 k k k,反之。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef double db;
const int Mx=55;
db f[Mx][Mx][Mx];
struct Task{
    db a,b;
}task[Mx];
int n;
long long ans;
bool cmp(Task a,Task b){return a.a>b.a;}
bool judge(db x){
    for(int i=0;i<=n+1;i++)
        for(int j=0;j<=n+1;j++)
            for(int k=0;k<=n+1;k++)
                f[i][j][k]=5e9;
    f[0][0][0]=0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=i;j++)
            for(int k=0;k<=i;k++)
                if(task[i].a<task[i-1].a){
                    f[i][j][0]=min(f[i][j][0],j-k+1>=0?f[i-1][j-k+1][k]:5e9);
                    f[i][j][1]=min(f[i][j][1],j-k>=0?f[i-1][j-k][k]+task[i].a-x*task[i].b:5e9);
                }else{
                    f[i][j][k]=min(f[i-1][j+1][k],k-1>=0?f[i-1][j][k-1]+task[i].a-x*task[i].b:5e9);
                }
    for(int j=0;j<=n;j++)
        for(int k=0;k<=n-j;k++)
            if(f[n][j][k]<=0)return 1;
    return 0;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%lf",&task[i].a);
    for(int i=1;i<=n;i++)scanf("%lf",&task[i].b);
    sort(task+1,task+1+n,cmp);
    task[0].a=-1e8;
    db l=0,r=5e9;
    for(int t=1;t<=100;t++){
        db mid=(l+r)/2;
        if(judge(mid))r=mid;
        else l=mid;
    }
    ans=ceil(l*1000);
    printf("%lld",ans);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值