Description
对于一个数列,其混乱度定义为连续相等的数的段数。如:1 2 1 2 1,其混乱度为5,而:1 2 2 3 3,其混乱度为3。现给出一个数列,允许取出k个数并允许插入数列中的任意一个位置,要求该数列的混乱度尽量小,并求出这个最小混乱度。
对于100%的数据:1 <= k <= n <= 100,所有数均在[25,32]内。
Solution
由于取出一个数i,你可以放在左边和右边,你不知道放在哪里才是最优的?
那么我们可以直接把要取的全部取出来,最后再插进数列中。
设F[i][j][k][s]表示做到第i个数,当前数列最后的数为j,取出了k个数,当前数列中数的集合为s的最小混乱度。
转移很简单:
1、第i+1个数取出:F[i+1][j][k+1][s] = min(F[i+1][j][k+1][s], F[i][j][k][s]);
2、第i个数放在最后面:F[i+1][a[i+1]][k][s|(1<<i)] = min(F[i+1][a[i+1]][k][s|(1<<i)], F[i][j][k][s]+(a[i+1] != j));
最后只需要把最后状态没有出现的数集合算上就好。
Code


1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <string> 5 #include <algorithm> 6 7 using namespace std; 8 9 #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i) 10 #define mset(a, b) memset(a, b, sizeof(a)) 11 int n, lim, a[105]; 12 int f[105][15][105][1<<8]; 13 int s_cnt; 14 bool vis[10]; 15 16 void Ckmin(int &AI, const int &BI) { if (AI > BI) AI = BI; } 17 18 int calc(int x) 19 { 20 int t_cnt = 0; 21 while (x > 0) 22 { 23 if (x&1) t_cnt ++; 24 x >>= 1; 25 } 26 return s_cnt-t_cnt; 27 } 28 29 int main() 30 { 31 scanf("%d %d", &n, &lim); 32 REP(i, 1, n) scanf("%d", &a[i]), a[i] -= 25; 33 REP(i, 1, n) if (!vis[a[i]]) vis[a[i]] = 1, s_cnt ++; 34 REP(i, 0, n) 35 REP(j, 0, 8) 36 REP(k, 0, lim) 37 REP(s, 0, (1<<8)-1) f[i][j][k][s] = 10000; 38 f[0][8][0][0] = 0; 39 REP(i, 0, n-1) 40 REP(j, 0, 8) 41 REP(k, 0, lim) 42 REP(s, 0, (1<<8)-1) 43 if (f[i][j][k][s] < 10000) 44 { 45 if (s&(1<<a[i+1])) 46 { 47 if (j == a[i+1]) Ckmin(f[i+1][j][k][s], f[i][j][k][s]); 48 else 49 { 50 if (k < lim) Ckmin(f[i+1][j][k+1][s], f[i][j][k][s]); 51 Ckmin(f[i+1][a[i+1]][k][s], f[i][j][k][s]+1); 52 } 53 } 54 else 55 { 56 if (k < lim) Ckmin(f[i+1][j][k+1][s], f[i][j][k][s]); 57 Ckmin(f[i+1][a[i+1]][k][s|(1<<a[i+1])], f[i][j][k][s]+1); 58 } 59 } 60 int ans = 10000; 61 REP(j, 0, 7) 62 REP(k, 0, lim) 63 REP(s, 0, (1<<8)-1) 64 Ckmin(ans, f[n][j][k][s]+calc(s)); 65 printf("%d\n", ans); 66 return 0; 67 }