牛客OI周赛3-提高组-B-1408[dp]

题意

给你长度为 (n) 的两个排列 (A,B) 组成的序列,求最少的交换相邻数字的次数使得 (A,B) 分别有序。

(nleq 2000) .

分析

  • 如果只有一个排列时最少交换次数为逆序对数,方案可从小到大枚举数字 (a_i),并向左移动直到前面的数有序且没有比 (a_i)大的数为止。

  • 发现当前序列的每个数字仍然会按照上述方式交换,否则逆序对仍要交换且还有多余的部分。

  • 只是两个序列的错杂交换会相互影响,于是考虑定义状态 (f_{i,j}) 表示 (A) 序列的前 (i) 个有序,(B) 序列的前 (j) 个有序(一定占据了前 ((i+j)) 个位置)最小的交换次数。每次枚举是哪一边交换并查询还需要和前面的多少个元素交换即可。

  • 总时间复杂度为 (O(n^2logn))

代码

#include<bits/stdc++.h>
using namespace std;
#define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].last,v=e[i].to)
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
typedef long long LL;
inline int gi(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch))	{if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
	return x*f;
}
template<typename T>inline bool Max(T &a,T b){return a<b?a=b,1:0;}
template<typename T>inline bool Min(T &a,T b){return b<a?a=b,1:0;}
const int N=4004 + 7,inf=0x3f3f3f3f;
int n;
int a[2][N],tr[N],f[N][N];
char s[10];
int lowbit(int x){return x&-x;}
void modify(int x,int y){for(int i=x;i<=2*n;i+=lowbit(i)) tr[i]+=y;}
int query(int x){int res=0;for(int i=x;i;i-=lowbit(i)) res+=tr[i];return res;}
int main(){
	n=gi();
	rep(i,1,2*n){
		int x;
		scanf("%s%d",s,&x);
		a[s[0]=='W'][x]=i;
		modify(i,1);
	}
	a[0][0]=a[1][0]=2*n+1;
	memset(f,0x3f,sizeof f);
	f[0][0]=0;
	rep(i,0,n){
		modify(a[0][i],-1);
		rep(j,0,n){
			modify(a[1][j],-1);
			if(i||j){
				if(i) Min(f[i][j],f[i-1][j]+query(a[0][i]-1));
				if(j) Min(f[i][j],f[i][j-1]+query(a[1][j]-1));
			}
		}
		rep(j,0,n) modify(a[1][j],1);
	}
	printf("%d
",f[n][n]);
	return 0;
}
原文地址:https://www.cnblogs.com/yqgAKIOI/p/9831003.html