[ABC328G] Cut and Reorder 题解
题目不难,思维难度尚可。
首先需要发现的性质是 \(1\) 操作的次数最多只需要使用一次,使用多少次其实都是等价的。
\(n\le 22\) 显然考虑状压 dp。平凡的想法是设 \(dp_{i,j}\) 表示填数的状态为 \(i\),最后一个填的是 \(j\) 位置的数的最小代价。这样的转移复杂度是 \(O(2^nn^2)\) 的,时间和空间上都难以接受。
考虑优化 \(j\) 维度。对于每个 \(i\),我们枚举 \(j\) 之后由于其转移的特殊性,直接从 \([j,n]\) 依次转移即可。
代码:
#include <bits/stdc++.h>
#define N 22
#define int long long
using namespace std;
int n;
int a[N], b[N];
int c;
int dp[(1 << N) + 5];
vector<int>v[N];
signed main() {
cin >> n >> c;
for (int i = 0; i < n; i++)
scanf("%lld", &a[i]);
for (int i = 0; i < n; i++)
scanf("%lld", &b[i]);
memset(dp, 0x3f, sizeof dp);
dp[0] = 0;
for (int i = 0; i < (1 << n) - 1; i++) {
int cnt = 0, x = i;
while (x) {
cnt += (x & 1);
x >>= 1;
}
v[cnt].push_back(i);
}
for (int nb = 0; nb < n; nb++)
for (auto sta : v[nb]) {
for (int i = 0; i < n; i++)
if (!((sta >> i) & 1)) {
int stb = sta, sum = 0;
for (int j = 0; j < min(n - i, n - nb); j++) {
int x = i + j;
if ((sta >> x) & 1)
break;
stb |= (1 << x);
sum += abs(a[nb + j] - b[x]);
if (sta)
dp[stb] = min(dp[stb], dp[sta] + sum + c);
else
dp[stb] = min(dp[stb], dp[sta] + sum);
}
}
}
cout << dp[(1 << n) - 1] << "\n";
return 0;
}
标签:Cut,sta,int,题解,nb,ABC328G,++,dp
From: https://www.cnblogs.com/Rock-N-Roll/p/18401942