描述
火神为了检验 zone 的力量,他决定单挑 n n n 个人。
由于火神训练时间有限,最多只有 t t t 分钟,所以他可以选择一部分人来单挑, 由于有小 y 的帮助,他得到了每个人特定的价值,每个人的价值由一个三元组 ( a , b , c ) (a,b,c) (a,b,c)组成,表示如果火神在第 x 分钟单挑这个人,他就会得到 a − b ∗ x a-b*x a−b∗x 的经验值, 并且他需要 c c c 分钟来打倒这个人。
现在火神想知道,他最多可以得到多少经验值,由于火神本来就很笨,进入 zone 的疯狂的火神就更笨了,所以他希望你来帮他计算出他最多可以得到多少 经验值。
输入
第一行一个正整数 T T T,表示数据组数。
对于每组数据,第一行为两个正整数 n n n 和 t t t,表示跟火神单挑的人的个数和 火神的训练时间。下面 n n n 行,每行三个正整数 A i , B i , C i Ai,Bi,Ci Ai,Bi,Ci,表示每个人的价值,含义见题目。
输出
对于每组数据输出一行一个整数,表示火神最多能得到多少经验值。
样例输入
1
4 10
110 5 9
30 2 1
80 4 8
50 3 2
样例输出
88
提示
- 对于 20%的数据满足: 1 ≤ n ≤ 10 1≤n≤10 1≤n≤10
- 对于 50%的数据满足: 1 ≤ n ≤ 18 1≤n≤18 1≤n≤18
- 对于 100%的数据满足: 1 ≤ n ≤ 1000 , 1 ≤ t ≤ 3000 , 1 ≤ C i ≤ t , A i ≤ 106 1≤n≤1000,1≤t≤3000,1≤Ci≤t,Ai≤106 1≤n≤1000,1≤t≤3000,1≤Ci≤t,Ai≤106
- 保证 n > 200 n>200 n>200 的数据组数不超过 5 5 5 组,其他的数据组数不超过 10 10 10 组
- 保证每个人贡献的经验值到训练结束都不会变成负数
解析:
可以看出,该题的原型是 01 01 01背包问题
首先可知并不是所有的人都要选入被挑战的队伍,则算法第一步便是按某种优先级排序:
对于两个人
i
i
i和
j
j
j谁先被挑战选其中更优的方案,设之前的选择都已达到最优,即挑战两人前所花的时间相同,又因为都要挑战
i
i
i和
j
j
j,所以结束对两人的挑战时所花时间也相同,设
t
t
t为挑战两人前则只需要判断
a
[
i
]
−
b
[
i
]
∗
(
c
[
i
]
+
t
)
+
a
[
j
]
−
b
[
j
]
∗
(
t
+
c
[
i
]
+
c
[
j
]
)
a[i]-b[i]*(c[i]+t)+a[j]-b[j]*(t+c[i]+c[j])
a[i]−b[i]∗(c[i]+t)+a[j]−b[j]∗(t+c[i]+c[j])与
a
[
j
]
−
b
[
j
]
∗
(
c
[
j
]
+
t
)
+
a
[
i
]
−
b
[
i
]
∗
(
t
+
c
[
j
]
+
c
[
i
]
)
a[j]-b[j]*(c[j]+t)+a[i]-b[i]*(t+c[j]+c[i])
a[j]−b[j]∗(c[j]+t)+a[i]−b[i]∗(t+c[j]+c[i])整理一下,就是判断
−
b
[
j
]
∗
c
[
i
]
- b[j] * c[i]
−b[j]∗c[i] 与
−
b
[
i
]
∗
c
[
j
]
- b[i] * c[j]
−b[i]∗c[j]的大小,取绝对值小的更优
排完序后再跑一遍
01
D
P
01DP
01DP就可以了
CODE
#include<bits/stdc++.h>
using namespace std;
int read(){
int s=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=getchar();
}
while(isdigit(ch)){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*f;
}
const int N=3e3+5;
struct fjy{
int a,b,c;
}l[205];
int f[N],vis[N],cz[N];
int m,n,t,ans;
bool com(const fjy &x,const fjy &y){
return x.c<y.c;
}
int max(int x,int y){
return x>y?x:y;
}
int main(){
// freopen("crazy.in","r",stdin);
// freopen("crazy.out","w",stdout);
m=read();
while(m--){
//queue<int> q;//记录需要处理的时间点
memset(cz,0,sizeof cz);
memset(f,0,sizeof f);
memset(vis,0,sizeof vis);
n=read(),t=read();
for(int i=1;i<=n;i++){
l[i].a=read(),l[i].b=read(),l[i].c=read();
}
/*sort(l+1,l+n+1,com);*/
for(int i=1;i<=n;i++)
for(int j=1;j<=n-1;j++)
if(l[j].b*l[j+1].c<l[j].c*l[j+1].b) swap(l[j],l[j+1]);
/*for(int i=1;i<=n;i++)
if(l[i].a-l[i].b*l[i].c>f[l[i].c])
{
if(!vis[l[i].c]) q.push(l[i].c);
f[l[i].c]=l[i].a-l[i].b*l[i].c;
cz[l[i].c]=i;
}
while(!q.empty()){
int x=q.front();q.pop();vis[x]=0;
for(int i=cz[x]+1;i<=n;i++)
if(f[cz[x]]+l[i].a-l[i].b*(l[i].c+cz[x])>f[l[i].c+cz[x]])
{
f[l[i].c+cz[x]]=f[cz[x]]+l[i].a-l[i].b*(l[i].c+cz[x]);
if(!vis[l[i].c+cz[x]]) q.push(l[i].c+cz[x]);
cz[l[i].c+cz[x]]=i;
}
}*/
for(int i=1;i<=n;i++){//按照优先顺序枚举
for(int j=t;j>=l[i].c;j--) f[j]=max(f[j],f[j-l[i].c]+l[i].a-l[i].b*j);//01背包只能装一次,若从小到大,可能被装入过多次
}
for(int i=t;i>0;i--) if(ans<f[i]) ans=f[i];
printf("%d\n",ans);ans=0;
}
// fclose(stdin);
// fclose(stdout);
return 0;
}