zoukankan      html  css  js  c++  java
  • codeforces#1215E. Marbles(状压DP)

    题目大意:给出一个由N个整数组成的序列,通过每次交换相邻的两个数,使这个序列的每个相同的数都相邻。求最小的交换次数。
    比如给出序列:1 2 3 2 1 ,那么最终序列应该是 1 1 2 2 3 ,最小交换次数为4.

    输入格式
    第一行输入一个数字n(2≤n≤4*10^5),表示大理石的总数。

    第二行输入n个数字a1,a2…,an(1≤ai≤20)表示第i块大理石的颜色为ai。

    输出格式
    最少交换的次数。

    样例
    样例输入 1
    7
    3 4 2 3 4 2 2
    样例输出 1
    3
    样例输入 2
    5
    20 1 14 10 2
    样例输出 2
    0
    样例输入 3
    13
    5 5 4 4 3 5 7 6 5 4 4 6 5
    样例输出 3
    21

    可以发现这道题的操作比较复杂,N的数量级也很大,(N^2)的时间复杂度肯定是行不通的。

    对于这种题,我们可以先通过几组样例来构思出整道题的模型。

    所以我们不妨考虑只有两种数的情况,对于数列 (11221),它的最终状态其实有两种(11122)(22111)。我们统计每个数字2前面与它不同的数字的个数,分别是 2 + 2共需4次,这就是将所有的数字2全部移到左边所需要的次数,同理统计1的话只需2次。

    现在我们来考虑多种数字的情况。我们发现这种情况不能直接通过上述方法来得出。但可以得到一个普遍的但不一定是最优的方法:
    每次将一种数字全部移到左边,再将第二种数字全部移到左边(这时我们不能考虑第一种数字)。。。依次类推。

    但每次移动哪种数字我们却不能确定,于是可以使用状压DP。

    因为题目中(1<=A_{i}<=20)。设当前状态为S,如果S的第(i)位为1,则第(i)种颜色已经被选过,统计答案时便不需要统计第(i)种。

    (大致思路已经给出,具体变量的含义请见注释)

    #include <bits/stdc++.h>
    using namespace std;
    
    #define N 400010
    #define M 30
    #define LL long long
    
    int A[N],n,pre[M][M];
    LL f[1<<21];
    vector<int> chos[M];
    
    int main() {
    	cin>>n;
    	for(LL i=1;i<=n;i++) {
    		cin>>A[i];
    		chos[A[i]].push_back(i);//统计每种数字的下标,放入vector储存
    	}
    	//预处理
    	for(LL i=1;i<=20;i++)
    		for(LL j=1;j<=20;j++) {
    			if(i==j || chos[i].size()==0 || chos[j].size()==0) continue;
    			//统计答案
    			for(LL k=0,v;k<chos[i].size();k++) {
    				v=chos[i][k];
    				if(v<chos[j][0]) continue;
    				pre[i][j]+=lower_bound(chos[j].begin(),chos[j].end(),v)-chos[j].begin();
    				//二分查找离当前数字最近的指定的下标,这就是需要交换的次数,进行累加
    			}
    		}
    	memset(f,0x3f,sizeof(f));
    	f[0]=0;
    	for(LL i=0;i<(1<<20);i++)
    		for(LL j=1;j<=20;j++) //DP部分
    			if(!(i&(1<<(j-1)))) {
    				LL sum=0;
    				for(LL k=0;k<20;k++)
    					if(i&(1<<k))
    						sum+=pre[k+1][j];
    				f[i|(1<<(j-1))]=min(f[i|(1<<(j-1))],f[i]+sum);
    			}
    	cout<<f[(1<<20)-1];
    }
    
  • 相关阅读:
    利用python在windows环境下爬取赶集网工作信息。
    扔骰子
    python 输入英语单词,查看汉语意思
    获取指定日期的上一个月日期
    爬取代理IP,并判断是否可用。
    递归实现 十进制转换其他进制(2-16)
    特殊回文数
    python 实现无序列表
    python 实现剪刀石头布(三局两胜)
    python 实现简单语音聊天机器人
  • 原文地址:https://www.cnblogs.com/MisakaMKT/p/11597366.html
Copyright © 2011-2022 走看看