某题目2 状压DP

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
", ans);
66     return 0;
67 }
View Code
原文地址:https://www.cnblogs.com/-ZZB-/p/6635659.html