【CF1215E】Marbles【状压DP】
傳送門
題意:給一個長為NNN的序列aaa,每次操作交換兩個相鄰位置,求最少操作次數使得所有相同的值連成一片。
N≤400000N \leq 400000N≤400000,ai≤20a_i \leq20ai?≤20
我們發現aia_iai?很小,盲猜單獨考慮
我們重新確認一個宏大的時空觀
把所有位置按值分組,然后一組一組加進去。在某一組沒有加之前,這個位置是不存在的,即1 3 2在沒有加3時,1 2是相鄰的。
設cnt[i][j]cnt[i][j]cnt[i][j]表示只有iii和jjj兩組數時,把所有iii移到jjj的前面的最小操作次數
這個可以暴力枚舉iii和jjj,然后雙指針即可
考慮狀壓
設dp[S]dp[S]dp[S]表示當前加入的數的狀態為SSS的最小操作次數 我們新加一個數時,把散裝的都移到最前面。因為cntcntcnt是單獨考慮的,所以加起來就可以了
即
dp[S]=min?i∈S{dp[S?{i}]+∑j∈S,i≠jcnt[i][j]}dp[S]=\min_{i \in S}\{dp[S-\{i\}]+\sum_{j\in S,i\neq j}cnt[i][j]\}dp[S]=i∈Smin?{dp[S?{i}]+j∈S,i?=j∑?cnt[i][j]}
最后的dp[2n?1]dp[2^n-1]dp[2n?1]即答案
復雜度O(na2+2aa2)O(na^2+2^aa^2)O(na2+2aa2)
#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #include <vector> using namespace std; typedef long long ll; inline int read() {int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans; } vector<int> v[20]; ll cnt[20][20],dp[1<<20]; int main() {int n=read();for (int i=1;i<=n;i++) v[read()-1].push_back(i);for (int i=0;i<20;i++)for (int j=0;j<20;j++)if (i!=j){int pos=-1;for (int k=0;k<v[i].size();k++){while (pos+1<v[j].size()&&v[j][pos+1]<v[i][k]) ++pos;cnt[i][j]+=pos+1;}}dp[0]=0;for (int s=1;s<(1<<20);s++){dp[s]=1e18;for (int i=0;i<20;i++)if (s&(1<<i)){ll sum=0;for (int j=0;j<20;j++)if (s&(1<<j))sum+=cnt[i][j];dp[s]=min(dp[s],dp[s^(1<<i)]+sum); } }cout<<dp[(1<<20)-1];return 0; }總結
以上是生活随笔為你收集整理的【CF1215E】Marbles【状压DP】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【LOJ166】拉格朗日插值2【拉格朗日
- 下一篇: 【NOIP2018】赛道修建【二分】【树