题目链接:https://atcoder.jp/contests/abc232/tasks/abc232_f
题目大意
给出两个长度为 n 的序列 A 和 B,有两个操作分为别:
- 在序列 A 中选择一个元素加一或减一,花费 x
- 在序列 A 中选择一个元素与其右边的元素交换(不能选择最后一个元素),花费 y
可以执行上述操作任意次,问让序列 A 等于序列B的最小花费
思路
状压DP。
定义:
d
p
x
dp_x
dpx 表示
x
x
x 在二进制位下,对应为
1
1
1 的位置
i
i
i 的
A
i
A_i
Ai 全部安排好的花费。
eg:
d
p
1
1
10
dp_{11_{10}}
dp1110 =
d
p
101
1
2
dp_{1011_2}
dp10112 表示
a
1
、
a
2
a_1、a_2
a1、a2 和
a
4
a_4
a4 放到
b
1
、
b
2
、
b
3
b_1、b_2、b_3
b1、b2、b3 上的最小花费。
状态转移方程看代码。
ACcode
#include<bits/stdc++.h>
#define ll long long
#define endl "\n"
using namespace std;
const int maxn = 1e6 + 5;
const int mod = 998244353;
ll dp[maxn];
ll a[100], b[100];
int main(){
ll n, x, y;
cin >> n >> x >> y;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= n; i++) cin >> b[i];
memset(dp, 0x3f, sizeof(dp));
dp[0] = 0;
for(int i = 0; i < (1<<n); i++){
int c = __builtin_popcount(i), d = 0;
// c 表示前边已经安排好几个a,需要把 a(j+1) 放到 b(c+1) 上
// d 表示要把 a(j+1) 放到 b(c+1) 的位置上要交换几次
// 也就是说有 d 个 a 放在了 a(j+1) 前边,需要交换 d 次
for(int j = n-1; j >= 0; j--){
if(i>>j&1) d++;
else dp[i|(1<<j)] = min(dp[i|(1<<j)], dp[i] + d*y + abs(a[j+1]-b[c+1])*x);
}
}
cout << dp[(1<<n)-1] << endl;
return 0;
}