codeforces 1215 E Marbles-----状压DP

Marbles

题意:长度4e5的数字序列,不同数字个数至多20个,每次操作可选取两个相邻数字并交换位置,现在想要使序列中所有相同数字都排列在一起,问至少需要几次操作。时限4s.

题解:由不同数字个数至多20应该想到状压DP......

​ 现在假想全部排完之后的状态,根据不同数字块的位置显然有\(20!\)种情况,那么我们可以假想数字1到20是一个一个完成合并并放到数列左端的。这样的话\(DP[i]\)就表示\(i\)数位上为\(1\)的数字完成合并并丢在左边所需操作次数。

​ 预处理一个\(val[i][j]\)表示把所有数字\(i\)放到所有数字\(j\)左边所需操作次数,那么很显然转移的增量就是这个数字与其他所有未完成合并的数字的\(val\)和。(好巧妙TuT)

​ 复杂度:o(\(2^{20}*400\)).

#include <bits/stdc++.h>
using namespace std;
int n;
vector<int> pos[20];
typedef long long ll;
ll dp[1<<20],val[20][20];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int x;
		scanf("%d",&x);
		pos[x-1].push_back(i);
	}
	for(int i=0;i<20;i++){
		for(int j=0;j<20;j++){
			if(i==j||pos[j].empty()) continue;
			for(int k=0;k<int(pos[i].size());k++){
				int x=pos[i][k];
				val[i][j]+=lower_bound(pos[j].begin(),pos[j].end(),x)-pos[j].begin();
			}
		}
	}
	memset(dp,0x3f3f3f3f,sizeof(dp));
	dp[0]=0;
	for(int i=0;i<(1<<20);i++){
		vector<int> u;
		for(int j=0;j<20;j++){
			if((1<<j)&i) continue;
			u.push_back(j);
		}
		for(int j=0;j<int(u.size());j++){
			int x=u[j];
			ll sum=0;
			for(int k=0;k<int(u.size());k++){
				if(u[k]!=x) sum+=val[x][u[k]];
			}
			dp[i|(1<<x)]=min(dp[i|(1<<x)],dp[i]+sum);
		}
	}
	cout << dp[(1<<20)-1];
	return 0;
}
posted @ 2019-09-16 12:45  rain_star  阅读(210)  评论(0编辑  收藏  举报