【递归/dp】Acwing1243.糖果(IDA*+步调函数/状态压缩+01背包)

题目

题解

动态规划

用二进制表示每种味道尝了没有,如:一共有五种味道,吃了前三种——00111,吃了五种——11111
每包糖果都可以选或不选
dp(i,j) i代表前i包糖果, j代表所能到达的状态(吃了几种种味)
其实就是01背包问题
dp[i-1][j & (~w[i])]+1 意味着:选这包糖果,从前面未选这包的状态+1包
同时不用担心重复问题
前面:1 2 3
后面:3 4 5
当前:j & (~w[i]) 会把 3, 4,5位上的1化为0,可前面应该是1,2,3位都为1才有的选,但通过这种运算,前面:00001也有的选,00011也有的选,00111也有的选

未状压(数组需要开到100*(2^20)超过百万,内存空间不够)

#include <iostream> #include <cstring> using namespace std;  const int N = 110;  int n,m,k; int dp[N][1000],w[22];  int main() {     cin >> n >> m >> k;     int t;     for(int i = 1; i <= n; ++i)     {         for(int j = 1; j <= k; ++j)         {             cin >> t;             w[i] |= (1 << t-1);         }     }          memset(dp,0x3f,sizeof dp);          dp[0][0] = 0;     int all  = (1 << m)-1;          for(int i = 1; i <= n; ++i)     {         for(int j = 0; j <= all; ++j)         {             dp[i][j] = min(dp[i-1][j],dp[i-1][j & (~w[i])]+1);         }     }     if(dp[n][all] >= 0x3f3f3f3f)         cout << -1 << endl;     else         cout << dp[n][all] << endl;     return 0; } 

状压

#include <iostream> #include <cstring> using namespace std;  const int N = (1<<20)+10;  int n,m,k; int dp[N],w[22];  int main() {     cin >> n >> m >> k;     int t;     for(int i = 1; i <= n; ++i)     {         for(int j = 1; j <= k; ++j)         {             cin >> t;             w[i] |= (1 << t-1);         }     }          memset(dp,0x3f,sizeof dp);          dp[0] = 0;     int all  = (1 << m)-1;          for(int i = 1; i <= n; ++i)         for(int j = all; j >= 0; j--)         {             dp[j] = min(dp[j],dp[j & (~w[i])]+1);         }     if(dp[all] >= 0x3f3f3f3f)         cout << -1 << endl;     else         cout << dp[all] << endl;     return 0; } 

IDA*+步调函数

太晚了题解明早更

#include <iostream> #include <vector> #include <algorithm> using namespace std;  const int N = 110, M = 1 << 20;  vector<int> col[30]; int log2[M]; int n, m, k;  //返回最右边1的位置,比如说:10100返回4(100) int lowbit(int x) {     return x & -x; }  //步调函数:返回至少需要再走几步 int h(int state) {     int res = 0;     for(int i = (1 << m) - 1 - state; i; i -= lowbit(i))     {         int c = log2[lowbit(i)];         res ++ ;         for(auto row : col[c])  i &= ~row;     }     return res; }  bool dfs(int depth, int state) {     if(depth == 0 || h(state) > depth) return state == (1 << m) - 1;          //优先选择方案数最少的     int t = -1;     //减去当前状态就可以得知缺少哪一种糖果,对剩下的糖果取方案数最少的     for(int i = (1 << m) - 1 - state; i; i -= lowbit(i))     {         int c = log2[lowbit(i)];         if( t == -1 || col[c].size() < col[t].size())             t = c;     }          for(int row : col[t])         if(dfs(depth-1,state | row))             return true;     return false; }  int main() {     cin >> n >> m >> k;     for(int i = 0; i < m; ++i) log2[1 << i] = i;  //log2可以找出每个1的列数     for(int i = 0; i < n; ++i)     {         int state = 0;         for(int j = 0; j < k; ++j)         {             int c;             cin >> c;             state |= 1 << c - 1;         }                  for(int j = 0; j < m; ++j)             if(state >> j & 1)                 col[j].push_back(state);     }          //去掉重复的行     for(int i = 0; i < m; ++i)     {         sort(col[i].begin(),col[i].end());         col[i].erase(unique(col[i].begin(),col[i].end()),col[i].end());     }          int depth = 0;     while(depth <= m && !dfs(depth,0)) depth++;     if(depth > m)  depth = -1;     cout << depth << endl;     return 0; }