heyicong
2025-01-22 16:25:01
校内模拟赛T3,赛时想到了正解的
赛后 T 了若干发之后终于过了。
本文提供一种非回退背包的解法。
在下文中,记
假设我们修改
考虑枚举
设
时间复杂度
我们发现,在第一种解法中,对于同一个物品,进行了太多次的背包 DP。我们尝试只进行一次DP,并标记为了凑出当前这个数,有哪些位置必须被选取。若不需要选取第
具体地,设
DP 完之后统计
时间复杂度仍然是
考虑优化一下求
由于所有状态都是 bitset
优化。最终复杂度
在代码中,dp[i][0]
代表文中的 dp[i][j]
代表文中的
int n, a[105], sum, cnt[105], mx, p;
bitset<105> dp[700005];
bitset<1400005> tmp;
int main() {
read(n);
for(int i = 1; i <= n; ++i)
read(a[i]), sum += a[i];
// 初始化 dp 数组
sort(a + 1, a + 1 + n);
dp[0] = 1, dp[1][0] = 0;
for(int i = 1; i <= n; ++i)
dp[1][i] = 1;
for(int i = 2; i <= sum; ++i)
dp[i] = dp[i - 1];
// 计算 dp 数组
for(int i = 1; i <= n; ++i)
for(int j = sum; j >= a[i]; --j)
if(dp[j - a[i]][0]) {
dp[j] &= dp[j - a[i]];
if(!dp[j][0]) dp[j][i] = 1;
dp[j][0] = 1;
}
// 统计 cnt 并计算 p
for(int i = 1; i <= sum; ++i)
if(dp[i][0])
for(int j = 1; j <= n; ++j)
if(!dp[i][j])
++cnt[j];
mx = p = 0;
for(int i = 1; i <= n; ++i)
if(cnt[i] > mx) mx = cnt[i], p = i;
// 计算 q
tmp[7000 * n] = 1;
for(int i = 1; i <= n; ++i)
if(i != p)
tmp = tmp | (tmp << a[i]) | (tmp >> a[i]);
for(int i = 1; i <= sum - a[p] + 1; ++i)
if(!tmp[7000 * n + i])
return printf("%d %d\n", a[p], i), 0;
}
这代码截止发题解当天竟然还跑出了洛谷最优解。(不过做这道题的人少。)