atcoder Keyence Programming Contest 2020 D - Swap and Flip 2020腾讯暑假实习笔试C(状压dp or 状压乱搞)
atcoder Keyence Programming Contest 2020 D - Swap and Flip 2020腾讯暑假实习笔试(状压dp or 状压乱搞)
题意
一张牌有正反两面,都有数字,有操作交换相邻两张卡牌,交换的时候两张牌都会翻转,问最少操作次数使得卡牌满足数字非降(n<=18)
分析
n<=18明示状压,首先要看什么样的序列是合法的
我们以0 1表示卡牌相对于初始状态是否翻转
1.1的数量为偶数,自己模拟一下就懂了
2.卡牌移动的距离和卡牌在那个位置上的正反是有关系的,奇数距离肯定是反面,偶数是正面
我们可以暴力枚举每张卡牌的01状态,一共\(2^n\)种状态,然后以以上条件判断合法后进行匹配,显然原序列每张牌和最左边一张可以匹配上的牌匹配是最优的,得出卡牌交换后每个序号所在的位置后,逆序数就是最小交换次数
复杂度\(2^n*n^2\)
因为本人状压比较拉胯,笔试的时候随便乱搞,估计是数据太水过了。。
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define F first
#define S second
#define mkp make_pair
#define pii pair<int,int>
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=2e5+5;
pair<int,int>pre[maxn],after[maxn];
int n;
int vis[maxn],a[maxn],b[maxn],p[maxn];
int solve(int s){
int cnt=0;
for(int i=0;i<n;i++)vis[i]=0;
for(int i=0;i<n;i++){
if((s>>i)&(1)){
cnt++;
pre[i]=mkp(b[i],1);
after[i]=pre[i];
}
else {
pre[i]=mkp(a[i],0);
after[i]=pre[i];
}
}
if(cnt&1)return inf;
cnt=0;
sort(after,after+n);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(after[j].first==pre[i].first&&((abs(i-j)%2)==pre[i].second)&&vis[j]==0){
vis[j]=1,cnt++,p[i]=j;
break;
}
}
}
if(cnt!=n)return inf;
int ans=0;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(p[j]<p[i])ans++;
}
}
return ans;
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",&a[i]);
for(int i=0;i<n;i++)scanf("%d",&b[i]);
int ans=inf;
for(int i=0;i<1<<n;i++){
ans=min(ans,solve(i));
}
printf("%d\n",ans==inf?-1:ans);
}