Codeforces 1215E. Marbles

传送门

注意到 $a$ 的值的数量并不大,考虑状压 $dp$

设 $f[S]$ 表示此时确定的数集合为 $S$ ,且按某种顺序从数列开头排列完成的最小交换次数

那么每个状态枚举最后一个填的数,加上代价后,取最小值即可

现在最大的问题是,代价怎么算...???

注意到我们每次交换相邻的两个数,这两个数和其他的数的相对位置是不变的(这个我认为是整题最关键的地方)

就是说在最优情况下,我们把数字 $x$ 统一交换到某个段时,产生的代价即为这个数原本每个位置和此时这个位置之前还没确定的数的数量....

讲得好绕啊,看代码比较好理解吧............

复杂度算一下达到了 $1e8$ 级别,但是 $CF$ 跑得快,这一题时限又长,所以很稳

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=4e5+7,M=(1<<20)+7;
int n,a[N],b[N],cnt[27];
ll cst[27][27],f[M];
// cst[i][j] 表示把值i统一交换到值j之前的代价
int main()
{
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=20;j++)
            cst[a[i]][j]+=cnt[j];
        cnt[a[i]]++;
    }
    int mx=(1<<20)-1;
    memset(f,0x3f,sizeof(f)); f[0]=0;
    for(int i=1;i<=mx;i++)
        for(int j=0;j<20;j++)
        {
            if(!((i>>j)&1)) continue;
            ll t=0;
            for(int k=0;k<20;k++)
            {
                if((i>>k)&1) continue;
                t+=cst[j+1][k+1];//代价和为 和此时还没确定的所有数交换的代价
            }
            f[i]=min(f[i],f[i^(1<<j)]+t);
        }
    printf("%lld\n",f[mx]);
    return 0;
}

 

posted @ 2019-09-17 11:38  LLTYYC  阅读(411)  评论(0编辑  收藏  举报