2025“钉耙编程”中国大学生算法设计春季联赛(1)1009 切割木材 对于状态合并更加深入的描述
状态合并与候选状态数量上限
在染染船长木材分割问题中,我们在动态规划过程中维护的候选状态用三元组表示:
\[(\text{dp值},\, \text{sand},\, \text{sor})
\]
其中:
- dp值 表示上一次分割时的最优值。
- sand 为当前区间所有数字的按位与,初始为全 \(1\)(即 \((1 \ll m)-1\))。
- sor 为当前区间所有数字的按位或,初始为 \(0\)。
候选状态更新
当加入新的数字 \(a[i]\) 时,每个候选状态更新为:
-
\[\text{sand} := \text{sand} \,\&\, a[i] \]
-
\[\text{sor} := \text{sor} \,|\, a[i] \]
更新后的状态对应区间 \((j+1, i)\) 的 \(f\) 值为
\[f = \text{sor} - \text{sand}
\]
候选状态对转移贡献为:
\[\text{dp}[j] + g(f)
\]
状态合并
-
原理:
当多个候选状态更新后若得到相同的 \((\text{sand}, \text{sor})\) 状态,由于 $$g(f)$$ 仅依赖于 $$f = \text{sor} - \text{sand}$$,不同状态对后续转移的贡献只差在 dp 值上。此时只保留 dp 值更大的状态即可。 -
实现:
遍历更新后的候选状态集合,若当前状态与前一状态的 \((\text{sand}, \text{sor})\) 相同,则更新前一状态的 dp 值为两者中的最大值;否则将当前状态加入集合。
为什么候选状态数始终维持在 \(O(m)\) 内?
-
单调性:
- 对于 sand,初始全 \(1\),随着区间扩展每个位只能由 \(1\) 变为 \(0\),一旦变 \(0\) 不再恢复。
- 对于 sor,初始为 \(0\),每个位只能由 \(0\) 变为 \(1\),一旦变 \(1\) 不会回退。
-
变化次数有限:
每个位仅会发生一次状态转变($$1 \to 0$$ 或 $$0 \to 1$$),所以从某个分割点开始,整个候选状态的 \((\text{sand}, \text{sor})\) 组合的变化最多有 \(m\) 次。 -
合并消除冗余:
通过合并那些更新后得到相同 \((\text{sand}, \text{sor})\) 的候选状态,只保留最优 dp 值,从而避免状态重复。最终,每一步候选状态的数量最多不会超过 \(O(m)\)。
总结
- 状态更新与合并: 每次加入新数字 \(a[i]\) 后,候选状态更新为
\[(\text{dp值},\, \text{sand} \,\&\, a[i],\, \text{sor} \,|\, a[i])
\]
若出现相同 \((\text{sand}, \text{sor})\) 状态,则仅保留 dp 值最大的状态。
- 状态数上限: 由于每个位的状态变化仅发生一次,总共 \(m\) 位,候选状态的变化次数受限,加上合并策略,确保候选状态始终在 \(O(m)\) 范围内,从而实现高效转移。
这种方法确保了动态规划在 $$O(n \times m)$$ 的时间复杂度内完成,适合 \(n\) 较大的情况(如 \(10^5\)),而 \(m\) 通常较小(最多 \(20\))。
code:
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define all(x) x.begin(),x.end()
#define rall(x) x.rbegin(),x.rend()
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int mod=998244353;
int gcd(int a,int b){return b?gcd(b,a%b):a;};
int qpw(int a,int b){int ans=1;while(b){if(b&1)ans=ans*a%mod;a=a*a%mod,b>>=1;}return ans;}
int inv(int x){return qpw(x,mod-2);}
void solve(){
int n,m;cin>>n>>m;
vector<int>a(n+1),g(1<<m),dp(n+1);
for(int i=1;i<=n;i++){cin>>a[i];}
for (int i=0;i<(1<<m);i++)cin>>g[i];
vector<tuple<int,int,int>>v;
v.pb({dp[0],(1<<m)-1,0});
for (int i=1;i<=n;i++) {
dp[i]=-1e18;
for (auto &[val,sand,sor]:v) {
int new_sand=sand&a[i];
int new_sor=sor|a[i];
dp[i]=max(dp[i],val+g[new_sor-new_sand]);
sand=new_sand;
sor=new_sor;
}
auto tmp=v;
v.clear();
for (auto &t:tmp) {
if (v.empty() ||
std::get<1>(v.back()) != std::get<1>(t) ||
std::get<2>(v.back()) != std::get<2>(t)) {
v.push_back(t);
}else {
get<0>(v.back())=max(get<0>(v.back()),get<0>(t));
}
}
v.emplace_back(dp[i], (1 << m) - 1, 0);
}
cout<<dp[n]<<'\n';
}
signed main(){
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int _=1;
cin>>_;
while(_--)solve();
}

浙公网安备 33010602011771号