Better experience
桌子上有 n n n 张卡牌按顺序从上到下摆放 , 初始时第一张已经解锁 , 其他的未解锁 , 每张卡牌上有一个值 a i a_i ai .
游戏按轮次进行. 每一轮 , 你需要选定一张解锁的卡牌 , 其值为 v v v , 并选择如下操作之一进行 :
每一轮结束后所选择的卡牌将会被丢弃.
游戏结束当且仅当无可选择的卡牌.
问可获得的最大点数.
1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105 , 0 ≤ a i ≤ n 0 \leq a_i \leq n 0≤ai≤n
bitset 优化背包, 复杂度 O ( n 2 / w ) O(n^2/w) O(n2/w)
这类直接操作有着后效性的题的入手点 : dp .
考虑维护什么状态可以消除后效性 , 得到朴素 dp 公式 :
dp[i][j]
表示考虑到第 i i i 张卡片 , 且前 j j j 张卡片被解锁时的最大分数.
显然 i i i 那一维可以删掉 , 这样就是一个 0/1 背包的形态.
但复杂度肯定不行 , 考虑优化 :
上述常见优化都不太行 , 因为枚举中的 i i i , j j j 都是必须量 , 无法减少 , 且转移方程非区间
而上述优化主要是通过维护额外信息来减少在区间上的枚举量.
这里需要一点灵感.
注意到实际上要么解锁 v v v 张卡片 , 要么获得 v v v 点数 , 这两个数量相等
这说明在固定 i i i 下 , 解锁卡片的数量加上获得的点数应该是一个定值 , 这个定值就是前 i i i 张卡片的值的和
进一步 , 当游戏结束后 ( i = n + 1 i=n+1 i=n+1) 如果知道了解锁卡片的数量 , 那么就可以算出获得的点数
所以得到了一个更简化的 dp 方程 :
dp[j]
表示 j j j 张卡片被解锁的情况是否存在.
复杂度并没有直观的下降 , 但有一点不同了 , dp 的值现在是 bool
.
所以可以使用 bitset
去优化背包的转移.
最后 , 如果假设解锁卡片的数量为 k k k , 那么答案就是
∑ i = 1 min ( k , n ) a i − k + 1 \sum_{i=1}^{\min(k,n)}a_i - k + 1 i=1∑min(k,n)ai−k+1
#include
using namespace std;
std::mt19937 rng(std::random_device{}());
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;
typedef const ll& cll;
typedef pair<int, int> pii;
typedef pair<int, ll> pil;
#define ls (loc<<1)
#define rs ((loc<<1)|1)
const int mod1 = 1e9+7;
const int mod2 = 998244353;
const int inf_int = 0x7fffffff;
const int hf_int = 0x3f3f3f3f;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;
int n;
int a[100100];
ll b[100100];
bitset<200001> v;
void solve(cint T) {
cin >> n;
for(int i=1; i<=n; i++) { cin >> a[i]; b[i] = a[i] + b[i-1]; }
v[1] = 1;
ll mx = 1;
ll ans = 0;
for(int i=1; i<=n && i<=mx; i++) {
v |= v << a[i];
mx += a[i];
if(v[i]) {
v[i] = 0;
ans = max(ans, b[min(n,i)] - i + 1);
}
}
for(int i=n+1; i<=n*2; i++) if(v[i]) {
ans = max(ans, b[min(n,i)] - i + 1);
}
cout << ans << '\n';
}
int main() {
//freopen("1.in", "r", stdin);
//cout.flags(ios::fixed); cout.precision(8);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T_=1;
//std::cin >> T_;
for(int _T=1; _T<=T_; _T++)
solve(_T);
return 0;
}