ABC232---F Simple Operations on Sequence
题目
题目简单来说就是给出两个序列A和B,通过两种操作使得序列A与B相同
操作:
1 . 选择\(a_i\)加或减1,需要X花费
2 . 交换\(a_i,a_{i+1}\),需要Y花费
题解
首先两个操作是可以独立操作的,我们可以先对序列A进行操作2
然后对操作完的序列A进行操作1使得A=B
进行完操作2后的序列\(A=\{a_{p_1},a_{p_2},···,a_{p_n}\}\),操作二的答案贡献是:
那么操作次数就是\(p_1,p_2,···,p_n\)的逆序对个数cnt,因为操作2可以相当于冒泡排序,而冒泡排序的次数就是序列的逆序对个数
则总共贡献为\(ans=cnt*Y\)
然后进行操作1,对答案贡献就是\(X*\sum_{i=1}^n|a_{p_i}-b_i|\)
则总共贡献为\(ans=cnt*Y+X*\sum_{i=1}^n|a_{p_i}-b_i|\)
那么如何求出最小ans
首先\(n\leq 18\)很小,可以考虑状态压缩
设\(dp_i\),记i的二进制位为1的个数为tot
\(dp_i\)为序列A的前tot个数已经确定了,并且前tot个数所对应的下标的2进制表示为i的最小花费
也就是选择序列A中tot个数(与其他数无关),对这tot个数进行操作使得这tot个数与序列B的前tot个数相同的最小花费
状态转移
当i&(1<<j) \(dp_i=min(dp_i,dp_{i-(1<<j)}+abs(a_{j+1}-b[tot])*X+a_j的逆序对个数*Y)\)
点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#define ll long long
using namespace std;
const int maxn=300001+101;
const int MOD=100007;
const int inf=2147483647;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int n,a[20],b[20];
ll x,y,dp[maxn];
int main(){
n=read();x=read();y=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)b[i]=read();
memset(dp,127,sizeof(dp));dp[0]=0;
for(int i=1;i<(1<<n);i++){
int tot=0;
for(int j=0;j<n;j++){if(i&(1<<j))tot++;}
int cnt=tot;
for(int j=0;j<n;j++){
if(i&(1<<j)){
dp[i]=min(dp[i],dp[i^(1<<j)]+abs(a[j+1]-b[cnt])*x+(tot-1)*y);
tot--;
}
}
}
printf("%lld",dp[(1<<n)-1]);
return 0;
}