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;
}
1011-Taxi
题目大意:
小Q有一张出租车会员卡,从第i
个城镇到第k
个城镇的减免为二者的曼哈顿距离和wk中的最小值。现在告诉你小Q位于的城镇,问接下来去哪个城镇的减免最多。
思路:
首先考虑没有wk限制的情况:
那么就是求|xi - xk| + |yi - yk|的最大值。
将其展开后得到以下式子:
那么我们只要维护xk + yk 、xk - yk 、-xk + yk 、-xk - yk 的最大值即可在O(1)的时间里得到最大的曼哈顿距离。
现在考虑有wk限制的情况:
首先将所有的城镇按照wk从小到大进行排序,同时维护xk + yk 、xk - yk 、-xk + yk 、-xk - yk的后缀最大值。
记排序后第k个城镇之后的最大曼哈顿距离为d
- 如果wk < d,那么对答案有贡献的是wk,越往后wk越大,
[1,k-1]
的城镇不会对答案有贡献,因此在[k+1,n]
这一段中继续寻找要去的城镇。 - 如果wk > d,那么对答案有贡献的是d,越往后wk越大,已经可以确定后面的城市对答案的最大贡献是d,因此在
[1,k-1]
这一段查找是否有城镇能贡献更大的值。 - 如果wk == d,那么答案就是d,此时不管往前还是往后,对答案的贡献都是减小的。
AC代码:
#include <bits/stdc++.h>
const int N = 1e5 + 5;
using namespace std;
struct town
{
int x, y, w;
} t[N];
bool cmp(town a, town b) { return a.w < b.w; }
int zz[N], zf[N], fz[N], ff[N]; //分别维护x+y x-y -x+y -x-y的后缀最大值
void solve()
{
int n, q;
cin >> n >> q;
for (int i = 1; i <= n; i++)
cin >> t[i].x >> t[i].y >> t[i].w;
sort(t + 1, t + 1 + n, cmp);
zz[n] = t[n].x + t[n].y;
zf[n] = t[n].x - t[n].y;
fz[n] = -t[n].x + t[n].y;
ff[n] = -t[n].x - t[n].y;
for (int i = n - 1; i >= 1; i--) //预处理后缀最大值
{
zz[i] = max(t[i].x + t[i].y, zz[i + 1]);
zf[i] = max(t[i].x - t[i].y, zf[i + 1]);
fz[i] = max(-t[i].x + t[i].y, fz[i + 1]);
ff[i] = max(-t[i].x - t[i].y, ff[i + 1]);
}
while (q--)
{
int ans = 0, l = 1, r = n, mid, d, x, y;
cin >> x >> y;
while (l <= r)
{
mid = (l + r) / 2;
d = max({x + y + ff[mid], x - y + fz[mid], -x + y + zf[mid], -x - y + zz[mid]});
if (t[mid].w < d)
{
ans = max(ans, t[mid].w);
l = mid + 1;
}
else if (t[mid].w > d)
{
ans = max(ans, d);
r = mid - 1;
}
else
{
ans = max(ans, d);
break;
}
}
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;
}