题目链接: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;
}
文章评论