zoukankan      html  css  js  c++  java
  • 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
    ",f[(1<<M)-1]);
       	}
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    
    
  • 相关阅读:
    Linux下PCI设备驱动程序开发 PCI驱动程序实现(三)
    一个动态内存管理模块的实现
    【Linux device driver】设备驱动程序概述(一)
    Linux PCI设备驱动程序开发 PCI 体系结构(一)
    [linux driver]用I/O命令访问PCI总线设备配置空间
    【Linux device driver】网络设备驱动程序(二)
    【Linux device driver】网络设备驱动注意的问题(三)
    C++中的引用、const引用和非const引用
    C++的数组和指针
    “指向const对象的指针” 和 “const指针”
  • 原文地址:https://www.cnblogs.com/mmlz/p/6172124.html
Copyright © 2011-2022 走看看