题意
有一堆任务,每个计算机可以跑一或两个。第 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÷bi≤x 等价于
a
i
−
b
i
∗
x
≤
0
a_i-b_i*x\le 0
ai−bi∗x≤0 于是可以判断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=1∑i∈{第一轮}ai−x∗i=1∑i∈{第一轮}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);
}
意思就是:
- 如果
a
i
<
a
i
−
1
a_i<a_{i-1}
ai<ai−1 ,那么跟
a
i
a_i
ai相等且放在第一轮的状态只有
0
0
0或
1
1
1,即
k
∈
{
0
,
1
}
k\in\{0,1\}
k∈{0,1},那么转移也就只有两种状态:
- 第 i i i项任务安排在第二轮,此时第三维无疑是 0 0 0,那么 k k k就变为了枚举 i − 1 i-1 i−1时有多少个任务与 a i − 1 a_{i-1} ai−1相等且安排在了第一轮。这些任务明显大于 a i a_i ai,于是就用上一状态加上任务 i i i放在第二轮的贡献(无)更新答案。
- 第 i i i项任务安排在第一轮,此时第三维是 1 1 1,那么 k k k也照样是枚举 i − 1 i-1 i−1时有多少个任务与 a i − 1 a_{i-1} ai−1相等且被安排在了第一轮。这些任务也是大于 k k k的,于是也是用上一轮的状态加上任务 i i i放在第一轮的贡献( a i − x ∗ b i a_i-x*b_i ai−x∗bi)更新答案。
- tips:
- 上一轮状态:因为枚举到的 k k k个任务比 a i a_i ai大了,所以现在枚举到的 j j j要减掉 k k k,然后再看当前(也就是第 i i i项)任务放第一轮还是第二轮.
- inf:把不可行的状态置成inf,使他不会更新之后的答案.
- 如果 a i = = a i − 1 a_i==a_{i-1} ai==ai−1 ,那么便枚举当前 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);
}