1002-Boss rush
题目大意:
Boss有H血量,现在有n个技能(1<=n<=18),每个技能都有持续伤害时间,持续时间内每秒都会造成伤害,同时释放每个技能后会进入冷却时间,冷却时间结束后才可以释放另一个技能,每个技能只能释放一次,技能在释放后就会造成伤害。问最少需要多少时间将Boss击败。
思路:
对于每个技能,先预处理伤害的前缀和。
因为技能只有18个,因此可以用二进制01表示一个技能是否释放。
但是这样无法引入时间的维度,因此考虑用二分枚举时间。
于是用dp[i]
表示i
中为对应位为1的技能释放所能造成的最大伤害,冷却总时间和技能释放顺序无关,可以根据i
中1的位来算出,状态转移方程也就很容易得到了。
AC代码:
#include <bits/stdc++.h>
const int N = 1e5 + 5;
using namespace std;
long long n, h;
int t[20], len[20];
long long dsum[20][N], dp[(1 << 18) + 5]; // dp[i]中为1的位表示释放了对应的技能
inline int get_t(int x)
{
int res = 0;
for (int i = 0; i < n; i++)
if ((1 << i) & x) res += t[i + 1];
return res;
}
bool check(int tm)
{
int bit, ts;
for (int i = 1; i < (1 << n); i++)
dp[i] = -1;
for (int i = 0; i < (1 << n); i++)
{
if (dp[i] < 0) continue;
if (dp[i] >= h) return 1;
ts = get_t(i);
if (ts >= tm) continue;
for (int j = 1; j <= n; j++)
{
bit = 1 << (j - 1);
if (!(bit & i))
{
if (ts + len[j] <= tm)
dp[i | bit] = max(dp[i] + dsum[j][len[j]], dp[i | bit]);
else
dp[i | bit] = max(dp[i] + dsum[j][tm - ts], dp[i | bit]);
}
}
}
return 0;
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
for (int i = 0; i < 20; i++)
dsum[i][0] = 0;
dp[0] = 0;
int T;
cin >> T;
while (T--)
{
int tsum = 0, maxlen = 0;
cin >> n >> h;
for (int i = 1; i <= n; i++)
{
cin >> t[i] >> len[i];
tsum += t[i];
maxlen = max(len[i], maxlen);
for (int j = 1; j <= len[i]; j++)
{
cin >> dsum[i][j];
dsum[i][j] += dsum[i][j - 1]; //伤害前缀和
}
}
int l = 1, r = tsum + maxlen + 1, mid, ans = -1;
while (l <= r)
{
mid = (l + r) / 2;
if (check(mid))
{
r = mid - 1;
ans = mid;
}
else
l = mid + 1;
}
cout << (ans == -1 ? -1 : ans - 1) << endl;
}
return 0;
}
1009-Package Delivery
题目大意:
有n个快递,每个快递有到达时间和最迟取件时间,去一次驿站能取k件快递,问最少几次可以把快递取完。
思路:
可以将快递看作区间[l,r]
贪心地想,每次选取r
最小的时间点去取快递一定是最优的。
因此分别对r
和l
进行排序,当取定一个ri
时,选取所有l
小于ri
的区间,用一个堆来维护,将其中r
最小的k个区间取出(也可能不足k)。然后重复以上过程直到快递取完。
AC代码:
#include <bits/stdc++.h>
const int N = 1e5 + 5;
using namespace std;
struct node
{
int r, idx;
};
bool operator<(node a, node b) { return a.r > b.r; }
pair<int, int> p[N];
int l[N], r[N];
bool vis[N];
bool cmpl(int a, int b) { return p[a].first < p[b].first; } //按照左端点对下标进行排序
bool cmpr(int a, int b) { return p[a].second < p[b].second; } //按照右端点对下标进行排序
void solve()
{
int n, k, ans = 0;
priority_queue<node> q;
cin >> n >> k;
for (int i = 1; i <= n; i++)
{
cin >> p[i].first >> p[i].second;
l[i] = i;
r[i] = i;
vis[i] = 0;
}
sort(l + 1, l + n + 1, cmpl); //按左端点对下标进行排序
sort(r + 1, r + n + 1, cmpr); //按右端点对下标进行排序
for (int i = 1, j = 1; i <= n; i++)
{
if (vis[r[i]]) continue;
while (j <= n && p[l[j]].first < p[r[i]].second) //将所有左端点小于当前右端点的快递入队
{
q.push({p[l[j]].second, l[j]});
j++;
}
ans++;
int cnt = k;
while (q.size() && cnt--) //取出k个快递
{
vis[q.top().idx] = 1;
q.pop();
}
}
cout << ans << "\n";
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--)
solve();
return 0;
}