• 状压DP的总结


    状压dp的标志

    ①数据小 ②通过题目所给出的条件以后得到的特征集合小

    一:CF259div2 D:

    题目大意:保证b[i]中每个数互质,给出a[i],然后求1~n的abs(a[i]-b[i])最小。a[i]<=30

    思路:首先得到b[i]必然小于60。这个很重要,因为我们枚举的b的集合就是60.首先当b如果都取1,那么最大的abs就是30,所以b可以取得的极限是60.然后我们可以得到60里面的所有的素数,这些素数的个数是17.因此我们发现是状压DP。

    首先预处理出1~60的包含素数的集合,然后枚举a[i],枚举1~60,再枚举1~1<<17即可得出答案。

    关键:寻找子集范围,寻找变量的最大范围

     1 #include<bits/stdc++.h>
     2 
     3 using namespace std;
     4 const int inf = 0x3f3f3f3f;
     5 const int maxn = 100 + 5;
     6 const int u = 1 << 17;
     7 struct point{
     8     int val, x, pre;
     9 }dp[maxn][u + 5];
    10 int f[maxn];
    11 bool vis[maxn];
    12 vector<int> v;
    13 int n;
    14 int a[maxn];
    15 vector<int> ans;
    16 
    17 int main(){
    18     for (int i = 2; i <= 60; i++){
    19         if (!vis[i]){
    20             v.push_back(i);
    21             for (int j = i + i; j <= 60; j += i){
    22                 vis[j] = true;
    23             }
    24         }
    25     }
    26     int len = v.size();
    27     //f[1] = 1;如果单独变成1了,那么dp时就无法有多个1了。
    28     for (int i = 1; i <= 60; i++){
    29         int tmp = i;
    30         for (int j = 0; j < len; j++){
    31             bool flag = false;
    32             while (tmp % v[j] == 0){
    33                 tmp /= v[j];
    34                 flag = true;
    35             }
    36             if (flag) f[i] |= 1 << (j);
    37         }
    38     }
    39     scanf("%d", &n);
    40     for (int i = 1; i <= n; i++) scanf("%d", a + i);
    41     for (int i = 0; i < u; i++) dp[0][i].val = 0;
    42     for (int i = 1; i <= n; i++){
    43         for (int j = 0; j < u; j++){
    44             dp[i][j].val = inf;
    45         }
    46     }
    47     for (int i = 1; i <= n; i++){
    48         for (int j = 1; j <= 60; j++){
    49             int x = f[j];
    50             for (int k = 0; k < u; k++){
    51                 if (k & x) continue;
    52                 if (dp[i][k | x].val > dp[i - 1][k].val + abs(a[i] - j)){
    53                     dp[i][k | x].val = dp[i - 1][k].val + abs(a[i] - j);
    54                     dp[i][k | x].pre = k;
    55                     dp[i][k | x].x = j;
    56                 }
    57             }
    58         }
    59     }
    60 
    61     int pre = 0, mini = dp[n][0].val;
    62     for (int i = 1; i < u; i++){
    63         if (mini >= dp[n][i].val){
    64             mini = dp[n][i].val;
    65             pre = i;
    66         }
    67     }
    68     //printf("%d
    ", dp[n][u - 1].val);
    69     for (int i = n; i > 0; i--){
    70         ans.push_back(dp[i][pre].x);
    71         pre = dp[i][pre].pre;
    72         //printf("dp[i][pre].pre = %d
    ", dp[i][pre].pre);
    73         //printf("pre = %d
    ", pre);
    74     }
    75     reverse(ans.begin(), ans.end());
    76     len = ans.size();
    77     for (int i = 0; i < len; i++){
    78         printf("%d%c", ans[i], i == len - 1 ? '
    ' : ' ');
    79     }
    80     return 0;
    81 }
    View Code

    然后上面的这个47-59行可以有优化成

    for (int i = 1; i <= n; i++){
            for (int j = 1; j <= 60; j++){
                int y = ((~f[j]) & (u - 1));
                int x = f[j];
                for (int k = y; ; k = (k - 1) & y){
                    if (dp[i][k | x].val > dp[i - 1][k].val + abs(a[i] - j)){
                        dp[i][k | x].val = dp[i - 1][k].val + abs(a[i] - j);
                        dp[i][k | x].pre = k;
                        dp[i][k | x].x = j;
                    }
                    if (k == 0) break;
                }
            }
        }
    View Code

    就是先筛选出其中本来就不包含和原子集取&的元素,然后通过枚举k,再&可以消去很多种情况,大大减少时间复杂度。

  • 相关阅读:
    JSON 数据转换
    .NET LINQ 数据排序
    .NET LINQ标准查询运算符
    UML 序列图
    UML 类图
    UML 用例建模
    UML 概述
    .NET LINQ查询语法与方法语法
    .NET LINQ查询操作中的类型关系
    .NET 反射概述
  • 原文地址:https://www.cnblogs.com/heimao5027/p/5677879.html
Copyright © 2020-2023  润新知