sjtu1590 强迫症

Description

BS96发布了一套有\(m\)个band柄绘的新badge,kuma先生想要拿到04的badge于是进行了抽抽抽。
kuma先生一共抽了\(n\)个badge。他把所有的badge排成一排来统计战果,出于强迫症他希望把所有相同band的badge放在一起。
kuma先生整理badge的方法是交换\(2\)个相邻的badge,现在他想知道他最少交换多少次可以达成目的。

Input

有多组数据,第一行一个数\(T\)表示数据组数,接下来每组数据有\(2\)行。
第一行两个数\(n,m\)
第二行\(n\)个数,表示每个badge的band编号。

Output Format

对于每组数据输出一行,形如”Case #X:Y”。X为数据组数,从\(1\)开始,\(Y\)为最少的交换次数。

Sample Input

3
4 2
1 2 1 2
6 4
2 1 4 3 1 2
8 6
1 3 2 5 5 4 5 2

Sample Output

Case #1: 1
Case #2: 6
Case #3: 5

Hints

\(40\%\), \(n \le 100, m \le 6\)
\(70\%\), n \le 1000, m \le 14
\(100\%\), n \le 100000, m \le 18

首先确定了最后次序之后,这个过程就是一个冒泡排序,最少交换次数即为逆序对数目。于是这道题目便变成了一道排列dp题目。我们可以用状态压缩dp来解决。
\(f[i]\)为状态为\(i\)时的最少逆序对数目。这个状态表示若\(a\)在集合\(i\)中,则\(a\)的最终位置已经确定排在前面一块。现在考虑我们将一个不在集合中的元素\(b\)加入集合中,我们只需要考虑\(b\)对逆序对的贡献,由于集合中已有元素最后都会放在\(b\)的前面,所以我们只需要计算在原数组中有多少\((p,q),q>p\),其中\(A_p = b,A_q \in i\)。对于特定的\(a,b\),这个贡献是可以预处理的。于是算法复杂度\(O(TM^22^M)\)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;

typedef long long ll;
#define maxn (100010)
#define maxm (20)
int T,N,M,A[maxn],suf[maxm][maxn],tmp[maxm]; ll f[1<<maxm],cor[maxm][maxm]; bool vis[1<<maxm];
     
inline int read()
{
	int F = 1,ret = 0; char ch;
	do ch = getchar(); while (!(ch >= '0'&&ch <= '9')&&ch != '-');
	if (ch == '-') F = -1,ch = getchar();
	do ret = ret*10+ch-'0',ch = getchar(); while (ch >= '0'&&ch <= '9');
	return F*ret;
}
     
inline void ready()
{
	for (int i = 1;i <= N;++i) suf[A[i]][i] = 1;
	for (int i = N-1;i;--i) for (int j = 1;j <= M;++j) suf[j][i] += suf[j][i+1];
	for (int i = 1;i <= N;++i)
		for (int j = 1;j <= M;++j)
			cor[A[i]][j] += (ll)suf[j][i];
}
          
inline void init()
{
	memset(vis,false,1<<M);
	memset(cor,0,sizeof(cor));
	for (int i = 1;i < (1<<M);++i) f[i] = 1LL<<50;
	for (int i = 0;i < M;++i) f[1<<i] = 0;
	for (int i = 1;i <= M;++i) memset(suf[i],0,4*(N+1));
	for (int i = 0;i < M;++i) vis[1<<i] = true;	
}
     
int main()
{
	freopen("1590.in","r",stdin);
	freopen("1590.out","w",stdout);
	T = read();
	for (int Case = 1;Case <= T;++Case)
   	{
   		printf("Case #%d: ",Case);
   		N = read(),M = read(); init();
   		for (int i = 1;i <= N;++i) A[i] = read();
   		ready();
   		for (register int i = 1,nn;i < (1<<M)-1;++i)
		{
			nn = 0;
			for (register int j = 0;j < M;++j) 	if (i&(1<<j)) tmp[++nn] = j+1;
			for (register int j = 0;j < M;++j)
				if (!(i&(1<<j)))
				{
					ll sum = f[i];
					for (register int k = 1;k <= nn;++k) sum += cor[j+1][tmp[k]];
					if (f[i|(1<<j)] > sum) f[i|(1<<j)] = sum;
				}
		}
		printf("%lld\n",f[(1<<M)-1]);
   	}
	fclose(stdin); fclose(stdout);
	return 0;
}

posted @ 2016-12-13 22:32  lmxyy  阅读(160)  评论(0编辑  收藏  举报