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
",f[(1<<M)-1]);
}
fclose(stdin); fclose(stdout);
return 0;
}