atcoder Keyence Programming Contest 2020 D 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
",ans==inf?-1:ans);
}