Finally, Little Q gets his weapon at level 10^5105 in the RPG game, now he is trying to beat the boss as soon as possible. The boss has HH units of health point (HP), Little Q needs to cause at least HH units of damage to beat the boss.
Little Q has learnt nn skills, labeled by 1,2,\dots,n1,2,…,n. Each skill can not be used multiple times, because there is not enough time for Little Q to wait for the skill to cool down. Assume Little Q uses the ii-th skill at the xx-th frame, the actor controlled by him will take t_iti frames to perform, which means Little Q will not be allowed to use other skills until the (x+t_i)(x+ti)-th frame. The length of the damage sequence of the ii-th skill is len_ileni, which means the skill will cause d_{i,j}di,j (0\leq j < len_i0≤j<leni) units of damage at the (x+j)(x+j)-th frame if Little Q uses the ii-th skill at the xx-th frame. Note that len_ileni can be greater than t_iti, for example, the burning skill can burn the boss for a long period, but takes a little time to cast the fire.
The game starts at the 00-th frame. Your task is to help Little Q beat the boss as soon as possible, or determine Little Q can't beat the boss using all the skills at most once.
Input
The first line contains a single integer TT (1 \leq T \leq 1001≤T≤100), the number of test cases. For each test case:
The first line contains two integers nn and HH (1 \leq n \leq 181≤n≤18, 1\leq H\leq 10^{18}1≤H≤1018), denoting the number of skills and the HP of the boss.
For each skill, the first line contains two integers t_iti and len_ileni (1 \leq t_i,len_i\leq 100\,0001≤ti,leni≤100000), the second line contains len_ileni integers d_{i,0},d_{i,1},\dots,d_{i,len_i-1}di,0,di,1,…,di,leni−1 (1\leq d_{i,j}\leq 10^91≤di,j≤109).
It is guaranteed that the sum of all len_ileni is at most 3\,000\,0003000000, and there are at most 55 cases such that n>10n>10.
Output
For each test case, output a single line containing an integer, denoting the first frame to beat the boss. If it is impossible to beat the boss, please print ''\texttt{-1}-1'' instead.
Sample
Input | Output |
---|---|
3 1 100 5 3 50 60 70 2 100 2 3 40 40 100 100 2 20 5 1 1000 1 1 999 | 1 2 -1 |
题意: 给出Boss的血量以及你可以使用的n个技能,每个技能最多用一次,并且每个技能使用完后有一段真空期ti不能释放其他技能,每个技能可以造成持续伤害,一共持续len秒,每秒造成di伤害,问最早击败Boss的时间,时间从0开始计算。
分析: 观察题目发现技能个数很小,所以猜测可以状压dp来枚举技能释放顺序,另外为了求最早击败时间,所以可以二分时间,然后check一下在这个时间内能否击败Boss,check函数里面就是一个状压dp求最大伤害的过程,这个过程和常规状压dp类似,就是枚举上一次释放的技能,只是在计算伤害的时候需要注意留给当前技能的剩余时间,设x表示二分的时间,t表示留给当前技能的时间,为了简化判断的过程,可以预处理出来一个数组time[i],记录i这个状态需要的真空期加和,那么t就等于x-time[上一个状态],t的取值有三种情况,第一种为负数,这说明现在还处于上一个技能的真空期中,那显然此时能造成的伤害就是上一个状态的伤害,第二种情况t∈[0, len],len是当前技能持续时间,这说明该技能伤害只打出来了一部分,此时造成伤害就是上一个状态伤害加该技能伤害的sum[t],第三种情况t > len,这说明该技能伤害全部打满,此时伤害就是上一个状态伤害加该技能sum[len],这三种情况考虑不全很容易出错!
每次更新完一个状态的伤害dp[i]后都可以和hp比较一下,如果大于等于hp那就说明x时间内可以击败Boss,如果没有状态能击败,那就说明x时间内不能击败Boss,最后输出二分结果即可。
具体代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#define int long long
using namespace std;
int n, hp, dp[1<<20], time[1<<20];
struct node{
int t, len;
int sum[100005];
}ski[20];
int st;
bool check(int x){//在x时间内是否可以ko
for(int i = 0; i < 1<<n; i++){
dp[i] = 0;
for(int j = 1; j <= n; j++){
if(i&(1<<(j-1))){
int t = x-time[i^(1<<(j-1))];
if(t < 0) dp[i] = max(dp[i], dp[i^(1<<(j-1))]);
else{
if(t > ski[j].len) t = ski[j].len;
dp[i] = max(dp[i], dp[i^(1<<(j-1))]+ski[j].sum[t]);
}
}
}
st = i;
if(dp[i] >= hp) return true;
}
return false;
}
signed main()
{
int T;
cin >> T;
while(T--){
scanf("%lld%lld", &n, &hp);
int l = 0, r = 0, ans = -1;
for(int i = 1; i <= n; i++){
scanf("%lld%lld", &ski[i].t, &ski[i].len);
r += max(ski[i].t, ski[i].len);
for(int j = 1; j <= ski[i].len; j++){
int t;
scanf("%lld", &t);
ski[i].sum[j] = ski[i].sum[j-1]+t;
}
}
for(int i = 0; i < 1<<n; i++){
time[i] = 0;
for(int j = 1; j <= n; j++){
if(i&(1<<(j-1)))
time[i] += ski[j].t;
}
}
while(l <= r){
int mid = l+r>>1;
if(check(mid)){
r = mid-1;
ans = mid;
}
else l = mid+1;
}
if(ans != -1) printf("%lld\n", ans-1);
else puts("-1");
}
return 0;
}