请问这份代码到底是 O(2nn2) 还是 O(2nn)。谢谢。
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int N = 23;
int n, c, a[N], b[N], dp[1 << 22 + 2];
int popcount(int x) {
if(x == 0 or x == 1) return x;
return popcount(x / 2) + x % 2;
}
signed main() {
memset(dp, 0x3f, sizeof dp);
cin >> n >> c;
for(int i = 1;i <= n; ++i) cin >> a[i];
for(int i = 1;i <= n; ++i) cin >> b[i];
dp[0] = 0;
for(int i = 0;i < (1 << n) - 1; ++i) {
int x = popcount(i);
// 接着 [1,i] 注意,可以随意选取一段
for(int l = 1;l <= n; ++l) {
int sub = i, sum = c, add = 1;
if(!(i & (1 << (l - 1))))
for(int k = l;k <= n; ++k) { // 连续的一段的转移 [l,r]
if(i & (1 << (k - 1))) break ;
sub |= 1 << (k - 1); add = k - l + 1;
sum += abs(b[k] - a[x + add]);
dp[sub] = min(dp[sub], dp[i] + sum);
}
}
}
cout << dp[(1 << n) - 1] - c << "\n"; // 段数 -1 刀
return 0;
}
/*
假设我们第一步直接重新乱切
后面就可以 O(n) 的计算答案
考虑状态的设计,
定义 dp{S} 表示我们现在已经确定了状态 S(s_i=1 说明 b_i 已经对应并计算)+
那些 b 的话,我们是用的 [1, i] 去填充的
现在我们采用刷表法进行转移
单次转移,我们就考虑一个连续段即可
然后 i = popcount(S),即 1 的个数
*/