1 条题解

  • 0
    @ 2026-4-18 23:41:40

    状压DP枚举子集类例题

    思路

    首先数据不大,可以状压,那就将 f[i]f[i] 表示 前 ii 个人过桥的最小时间, 通常我们可以设置一个断点 jj, 只需要知道 jjii 之间所需要的时间即可,那么转移式有

    f[i]=min{f[j]+mt[i xor j]}f[i] = min\{f[j]+mt[i\ xor\ j]\}

    其中 i xor ji\ xor\ j 表示 ii 种剩余的子集, 前提是 jjii 的子集

    这样题目就做完了

    如何枚举子集

    for (s0=s;s0;s0=(s0-1)&s) 依次枚举 ss 的子集

      for (int s=0;s<=mx;s++)
       for (int s0=s;s0;s0=(s0-1)&s)
       
    

    证明

    我们枚举一下

    s=1010s=1010 此时 s0=1010s_0=1010

    减一得 s0=1001s_0=1001

    ss 与得 s0=1000s_0=1000

    减一得 s0=0111s_0=0111

    ss 与得 s0=0010s_0=0010

    减一得 s0=0001s_0=0001

    ss 与得 s0=0000s_0=0000

    这样我们得到了集合 ss 的所有子集 s0s_0{11,10,01,00}\{11,10,01,00\}特别神

    时间复杂度

    那么时间复杂度是什么

    O(3N)O(3^N)

    推导:对于有着 kk11 的二进制数字,枚举子集需要的时间复杂度为 2k2^k , 拥有 kk11 的数字的数量用组合数学可知:(nk)\dbinom{n}{k}

    那么总的时间复杂度为:k=0nC(n,i)×2k\sum_{k=0}^nC(n,i)\times2^k

    这里给出二项式定理

    $(x+y)^n=\sum_{k=0}^n\dbinom{n}{k}\times x^{(n-k)}\times y^k$

    我们将这里的 xx 默认为 11, 则 yy 就等于 22

    那么时间复杂度就为 (1+3)n=O(3n)(1+3)^n=O(3^n)

    Code

    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define ll long long
    using namespace std;
    
    const int A = 1e5 + 11;
    const int B = 16;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    
    inline int read() {
      char c = getchar();
      int x = 0, f = 1;
      for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
      for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
      return x * f;
    }
    
    int a,b,t[1<<B],w[1<<B],f[1<<B], mt[1<<B], mw[1<<B];
    
    int main() {
    //  freopen(".in", "r", stdin);
    //  freopen(".out", "w", stdout);
      cin>>a>>b;
      int mx=(1<<b)-1;
      for (int i=1;i<=b;i++) cin>>t[i]>>w[i];
      for (int i=0;i<=mx;i++)
      {
        for (int j=1;j<=b;j++)
          if(i&(1<<(j-1))){
            mt[i]=max(mt[i], t[j]);
            mw[i]+=w[j];
          }
      }
      memset (f,0x3f,sizeof(f));
      f[0]=0;
      for (int i=0;i<=mx;i++)
      {
        for (int j=i;;j=(j-1)&i)
        {
          if(mw[i^j]<=a) f[i]=min(f[i],f[j]+mt[i^j]);
          if(!j) break;
        }
      }
      cout<<f[mx];
      fclose(stdin);
      fclose(stdout);
      return 0;
    }
    
    
    
    • 1

    信息

    ID
    3738
    时间
    1000ms
    内存
    128MiB
    难度
    10
    标签
    递交数
    3
    已通过
    2
    上传者