CF1215E Marbles

 题目

有 n (n \le 4 * 10^5)(n≤4∗105) 个珠子 , 第ii个珠子颜色是c_i (c_i \le 20)ci​(ci​≤20) , 每次操作把相邻的两个珠子交换。现在要把相同颜色的珠子排列在相连的一段,问至少要多少次操作 。

输入格式
第一行:一个数n

第二行:n个数,表示珠子的颜色

输出格式
一行:至少交换的次数

输入输出样例
输入 #1 复制

7
3 4 2 3 4 2 2
3

5
20 1 14 10 2

0

13
5 5 4 4 3 5 7 6 5 4 4 6 5

21

分析&&题解

看到ai<=20,可以用状压dp

同时,这道题可以发现与逆序对是有关的(参考归并排序)

把题目转化一下,我们把同种颜色重新赋一个权值,那么最后的目标顺序其实可以是一个从小到大权值排序的序列,那么对原数组重新赋值后,就是逆序对了

定义dp[i]表示赋权值方式为i的情况所得到的逆序对个数,且我们赋权值的时候后赋值的值要比前面的大

如有一个原序列{3,1,2,2},那么dp[3]=dp[(011)] = 0 , 方法是将1赋值为1,2赋值为2,是从dp[2] = dp[(010)] = 0转移过来

那么我们转移的时候就是枚举一种颜色k,然后将这个颜色去掉后的状态j,那么就是dp[j]+k与前面的点的逆序对

简单来说,定义w[i][j]表示存在a_{l}= i , a_{r}=j,l<r的个数

那么状态转移就是dp_i = min( dp_j + \sum_{v\in j}w[k][j] )

代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <climits>
#include <cstdlib>
using namespace std;
const int MAXN = 4e5 + 3;
int n , a[MAXN] ;
long long w[23][23];
long long dp[1<<20+2];
long long cnt[MAXN];
int main(){
    scanf( "%d" , &n );
    for( int i = 1 ; i <= n ; i ++ ){
        scanf( "%d" , &a[i] );cnt[a[i]-1] ++;
        for( int j = 0; j < 20 ; j ++ )
            w[j][a[i]-1] += cnt[j];
    }
    //memset( dp , LONG_MAX , sizeof( dp ) );
    dp[0] = 0;
    for( int i = 1 ; i < ( 1 << 20 ) ; i ++ ){
        dp[i] = LLONG_MAX;
        for( int j = 0 ; j < 20 ; j ++ ){
            if( i & ( 1 << j ) ){
                int k = i ^ ( 1 << j );
                long long sum =0 ;
                for( int l = 0 ; l < 20 ; l ++ ){
                    if( l != j && ( k & ( 1 << l ) ) ){
                        sum += w[j][l];
                    }
                }
                dp[i] = min( dp[i] , dp[k] + sum );
            }
        }
    }
    printf( "%lld" , dp[(1<<20)-1] );
}

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页