1 条题解
-
0
思路与算法
令1表示动物保留。预计算每个起点E和其5位窗口的快乐人数:至少一个喜欢位为1或害怕位为0就快乐。枚举最初4个围栏状态,在长度4的滑动掩码上DP,每步追加下一位并计当前窗口收益。最后4步追加位必须等于最初4位,闭合圆环后取最大值。
正确性说明
每个游客的条件完全取决于其5个连续围栏,预处理准确反映题目两个条件的析取。给定前4位后,每个后续位选择恰对应一种完整移除方案,DP记录同一4位边界下的最大已结算收益,历史其他位不再影响未来。末尾强制回到起始4位,确保跨尾首窗口也按同一个圆环计分,枚举16个起始状态覆盖全部方案。
复杂度
预处理O(32C),DP为O(16×N×16×2),即O(C+N)但常数需保留。空间O(32N+16),初始化所有窗口计数O(32N),已包含。N≤10000、C≤50000,计数int足够。
易错点
害怕动物被移走就快乐,不是所有害怕动物都要移走。喜欢与害怕按题目OR判断。E接近N时围栏号需取模,最后4步不能自由选。独立小规模枚举全部2^N保留方案;最大规模用每点3喜欢/2害怕的独立投票及全喜欢构造核验。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, c; cin >> n >> c; vector<array<int, 32>> reward(n); for (int i = 0; i < c; ++i) { int e, F, L; cin >> e >> F >> L; --e; int fear = 0, like = 0; for (int j = 0, x; j < F; ++j) { cin >> x; fear |= 1 << ((x - 1 - e + n) % n); } for (int j = 0, x; j < L; ++j) { cin >> x; like |= 1 << ((x - 1 - e + n) % n); } for (int mask = 0; mask < 32; ++mask) reward[e][mask] += bool((mask & like) || ((~mask) & fear)); } int answer = 0, NEG = -1000000000; for (int initial = 0; initial < 16; ++initial) { array<int, 16> dp; dp.fill(NEG); dp[initial] = 0; for (int e = 0; e < n; ++e) { array<int, 16> next; next.fill(NEG); for (int state = 0; state < 16; ++state) if (dp[state] > NEG) for (int b = 0; b < 2; ++b) { if (e + 4 >= n && b != ((initial >> (e + 4 - n)) & 1)) continue; int mask = state | (b << 4), ns = (state >> 1) | (b << 3); next[ns] = max(next[ns], dp[state] + reward[e][mask]); } dp = next; } answer = max(answer, dp[initial]); } cout << answer << '\n'; }
- 1