题目大意
给出 n n n 种苦工,每一种苦工有 A i A_{i} Ai 和 B i B_{i} Bi ,表示每一个 i i i 种苦工单位时间内可以获取 A i A_{i} Ai 点资源,而招募一个消耗的资源为 B i B_{i} Bi 点,给出初始资源 m m m 和目标资源 T T T 求最少的单位时间使资源到达 T T T 是多少。(招募不消耗时间且数据保证有解)
数据范围
对于30%的数据, N ≤ 10 , M , T ≤ 300 N \le 10, M, T \le 300 N≤10,M,T≤300
对于100%的数据, N ≤ 100 , M , T ≤ 1000 , A , B ≤ 2 31 N \le 100,M, T \le 1000, A, B \le 2^{31} N≤100,M,T≤1000,A,B≤231
思路
看到数据范围,而题目所求又很像背包问题,我们可以尝试用
D
P
DP
DP 解决此问题。
因为苦工可能到后面有资源才有可能能被招募到,所以以苦工种类为状态下标是不现实的,我们可以尝试转换思路。
因为题目的最少单位时间和效率以及目前资源有关,所以我们设
f
i
,
j
f_{i,j}
fi,j 表示现在的效率为
i
i
i (即一个单位时间可以获取
i
i
i 点资源), 剩余的资源有
j
j
j 点的最短单位时间是多少,明显可以通过一下转移方程得来:
f
i
+
A
x
,
j
−
B
x
=
min
(
f
i
,
j
)
f_{i+A_{x},j-B_{x}}= \min(f_{i,j})
fi+Ax,j−Bx=min(fi,j) (招募一个
x
x
x 种的苦工且苦工的消耗小于
j
j
j )
f
i
,
j
+
i
=
min
(
f
i
,
j
+
1
)
f_{i,j+i}= \min(f_{i,j}+1)
fi,j+i=min(fi,j+1) (等待一个单位的时间使资源增加
i
i
i)
状态要枚举
i
,
j
i,j
i,j 以及苦工种类
x
x
x ,理论时间复杂度是
O
(
n
T
2
)
O(nT^2)
O(nT2) 的,但是跑不满,可以通过一些剪枝(如过下一次等待可以达到的话就没有必要再招募了)优化一下,还是可以过的。
注意:要是一开始的资源大于目标的资源,直接输出
0
0
0 。
AC代码
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,t,ans;
int f[3030][3030],A[220],B[220],Inf;
int main()
{
scanf("%d %d %d",&n,&m,&t);
for(int i=1;i<=n;i++)
scanf("%d %d",&A[i],&B[i]);
if(m>t)
{
printf("0\n");
return 0;
}
memset(f,127/3,sizeof(f));
Inf=f[0][0],ans=Inf;
f[0][m]=0;
for(int i=0;i<=1000;i++)
{
for(int j=0;j<t;j++)
{
f[i][j+i]=min(f[i][j+i],f[i][j]+1);
if(i+j>=t)
ans=min(ans,f[i][j+i]);
if(i+i+j>=t)
ans=min(ans,f[i][j+i]+1);
}
for(int k=1;k<=n;k++)
{
for(int j=t-1-i;j>=A[k];j--)
{
if(f[i][j]==Inf)
continue;
f[i+B[k]][j-A[k]]=min(f[i+B[k]][j-A[k]],f[i][j]);
}
}
}
printf("%d\n",ans);
}