• 状态压缩DP及其拓展


    蒙德里安的梦想

    题目 蒙德里安的梦想https://www.acwing.com/problem/content/293/

    超级详细解析https://lishizheng.blog.csdn.net/article/details/112706309

    1. /*
    2. 下文对 if ((j & k ) == 0 && st[ j | k] ) 有清晰的解释!!!
    3. */
    4. #include
    5. using namespace std;
    6. const int N = 12, M = 1<< N;
    7. long long f[N][M] ;// 第一维表示列, 第二维表示所有可能的状态
    8. bool st[M]; //存储每种状态是否有奇数个连续的0,如果奇数个0是无效状态,如果是偶数个零置为true。
    9. vector<int > state[M]; //二维数组记录合法的状态
    10. //vector> state(M); //两种写法等价:二维数组
    11. int m, n;
    12. int main() {
    13. while (cin >> n >> m, n || m) //读入n和m,并且不是两个0即合法输入就继续读入
    14. {
    15. //第一部分:预处理1
    16. //对于每种状态,先预处理每列不能有奇数个连续的0
    17. for(int i = 0; i < (1 << n); i ++)
    18. {
    19. int cnt = 0 ;//记录连续的0的个数
    20. bool isValid = true; // 某种状态没有奇数个连续的0则标记为true
    21. for(int j = 0; j < n; j ++) //遍历这一列,从上到下
    22. {
    23. if ( (i >> j) & 1)
    24. {
    25. //i >> j位运算,表示i(i在此处是一种状态)的二进制数的第j位;
    26. // &1为判断该位是否为1,如果为1进入if
    27. if (cnt & 1)
    28. {
    29. //这一位为1,看前面连续的0的个数,如果是奇数(cnt &1为真)则该状态不合法
    30. isValid =false;
    31. break;
    32. }
    33. cnt = 0; // 既然该位是1,并且前面不是奇数个0(经过上面的if判断),计数器清零。
    34. //其实清不清零没有影响
    35. }
    36. else cnt ++; //否则的话该位还是0,则统计连续0的计数器++。
    37. }
    38. if (cnt & 1) isValid = false; //最下面的那一段判断一下连续的0的个数
    39. st[i] = isValid; //状态i是否有奇数个连续的0的情况,输入到数组st中
    40. }
    41. //第二部分:预处理2
    42. // 经过上面每种状态 连续0的判断,已经筛掉一些状态。
    43. //下面来看进一步的判断:看第i-2列伸出来的和第i-1列伸出去的是否冲突
    44. for (int j = 0; j < (1 << n); j ++) //对于第i列的所有状态
    45. {
    46. state[j].clear(); //清空上次操作遗留的状态,防止影响本次状态。
    47. for (int k = 0; k < (1 << n); k ++) //对于第i-1列所有状态
    48. {
    49. if ((j & k ) == 0 && st[ j | k])
    50. // 第i-2列伸出来的 和第i-1列伸出来的不冲突(不在同一行)
    51. //解释一下st[j | k]
    52. //已经知道st[]数组表示的是这一列没有连续奇数个0的情况,
    53. //我们要考虑的是第i-1列(第i-1列是这里的主体)中从第i-2列横插过来的,
    54. //还要考虑自己这一列(i-1列)横插到第i列的
    55. //比如 第i-2列插过来的是k=10101,第i-1列插出去到第i列的是 j =01000,
    56. //那么合在第i-1列,到底有多少个1呢?
    57. //自然想到的就是这两个操作共同的结果:两个状态或。 j | k = 01000 | 10101 = 11101
    58. //这个 j|k 就是当前 第i-1列的到底有几个1,即哪几行是横着放格子的
    59. state[j].push_back(k);
    60. //二维数组state[j]表示第j行,
    61. //j表示 第i列“真正”可行的状态,
    62. //如果第i-1列的状态k和j不冲突则压入state数组中的第j行。
    63. //“真正”可行是指:既没有前后两列伸进伸出的冲突;又没有连续奇数个0。
    64. }
    65. }
    66. //第三部分:dp开始
    67. memset(f, 0, sizeof f);
    68. //全部初始化为0,因为是连续读入,这里是一个清空操作。
    69. //类似上面的state[j].clear()
    70. f[0][0] = 1 ;// 这里需要回忆状态表示的定义
    71. //按定义这里是:前第-1列都摆好,且从-1列到第0列伸出来的状态为0的方案数。
    72. //首先,这里没有-1列,最少也是0列。
    73. //其次,没有伸出来,即没有横着摆的。即这里第0列只有竖着摆这1种状态。
    74. for (int i = 1; i <= m; i ++) //遍历每一列:第i列合法范围是(0~m-1列)
    75. {
    76. for (int j = 0; j < (1<//遍历当前列(第i列)所有状态j
    77. {
    78. for (auto k : state[j]) // 遍历第i-1列的状态k,如果“真正”可行,就转移
    79. f[i][j] += f[i-1][k]; // 当前列的方案数就等于之前的第i-1列所有状态k的累加。
    80. }
    81. }
    82. //最后答案是什么呢?
    83. //f[m][0]表示 前m-1列都处理完,并且第m-1列没有伸出来的所有方案数。
    84. //即整个棋盘处理完的方案数
    85. cout << f[m][0] << endl;
    86. }
    87. }

    小国王【线性状压DP+滚动数组优化+目标状态优化】  

    题目 小国王http://ybt.ssoier.cn:8088/problem_show.php?pid=1592

    解析 https://www.acwing.com/solution/content/56348/

     

    1. #include
    2. using namespace std;
    3. const int N=12,M=1<
    4. int cnt;//同一行合法状态的个数
    5. int s[M];//同一行合法状态集
    6. int num[M];//每个合法状态包含国王个数
    7. long long f[N][C][M]; //f[i][j][a] 表示前i行放了j个国王,第i行第a个状态时的方案数
    8. int main()
    9. {
    10. int n,k;//棋盘行数 国王总数
    11. cin>>n>>k;
    12. for(int i=0;i<(1<//枚举一行所有状态
    13. {
    14. if(!(i&i>>1))//如果不存在相邻的1
    15. {
    16. s[cnt++]=i;//保存一行的合法状态
    17. }
    18. for(int j=0;j
    19. num[i]+=(i>>j&1);// 统计每个合法状态的国王数量
    20. }
    21. f[0][0][0]=1;//不放国王也是一种方案
    22. for(int i=1;i<=n+1;i++)//枚举行
    23. for(int j=0;j<=k;j++)//枚举国王数
    24. for(int a=0;a//枚举第i行的合法状态
    25. for(int b=0;b//枚举第i-1的合法状态
    26. {
    27. int c=num[s[a]];//第i行第a个状态的国王数
    28. if((j>=c)&&!(s[b]&s[a])&&!(s[b]&(s[a]<<1))&&!(s[b]&(s[a]>>1)))//如果不存在同列的1、斜对角的1,可以继续放国王
    29. f[i][j][a]+=f[i-1][j-c][b];//从第i-1行向第i行转移
    30. }
    31. cout<1][k][0]<//第n+1行什么都不放,相当于只在1~n行放国王
    32. }

    玉米田【线性状压DP】

     题目 玉米田 https://www.acwing.com/problem/content/description/329/

     题解https://www.acwing.com/solution/content/56822/

     

     

    更新

    1. #include
    2. using namespace std;
    3. const int N=14,M=1<1e8;
    4. int f[N][M];
    5. int g[N];
    6. int s[M];
    7. int cnt;
    8. int main()
    9. {
    10. int n,m;
    11. cin>>n>>m;
    12. for(int i=1;i<=n;i++)
    13. for(int j=1;j<=m;j++)
    14. {
    15. int x;
    16. cin>>x;
    17. g[i]=(g[i]<<1)+x;
    18. }
    19. for(int i=0;i<(1<
    20. {
    21. if(!(i&i>>1))
    22. s[cnt++]=i;
    23. }
    24. f[0][0]=1;
    25. for(int i=1;i<=n+1;i++)
    26. for(int a=0;a
    27. for(int b=0;b
    28. {
    29. if((s[a]&g[i])==s[a]&&!(s[a]&s[b]))
    30. f[i][a]=(f[i][a]+f[i-1][b])%mod;
    31. }
    32. cout<1][0]<
    33. }

     炮兵阵地 

    题目  炮兵阵地

    解析 

    1. #include
    2. using namespace std;
    3. const int N=110,M=1<<10;
    4. int f[2][M][M];
    5. int g[N];
    6. vector<int> state;
    7. int cnt[M];
    8. int n,m;
    9. bool check(int s)
    10. {
    11. for(int i=0;i
    12. if((s >> i & 1)&&((s>>i+1&1)||(s>>i+2&1)))
    13. return false;
    14. return true;
    15. }
    16. int count(int s)
    17. {
    18. int res=0;
    19. while(s)
    20. {
    21. res+=s&1;
    22. s>>=1;
    23. }
    24. return res;
    25. }
    26. int main()
    27. {
    28. cin>>n>>m;
    29. for(int i=0;i
    30. for(int j=0;j
    31. {
    32. char a;
    33. cin>>a;
    34. if(a=='H')g[i]+=1<
    35. }
    36. for(int i=0;i<1<
    37. {
    38. if(check(i))
    39. {
    40. state.push_back(i);
    41. cnt[i]=count(i);
    42. }
    43. }
    44. for(int i=0;i2;i++)
    45. for(int j=0;jsize();j++)
    46. for(int k=0;ksize();k++)
    47. for(int u=0;usize();u++)
    48. {
    49. int a=state[u],b=state[j],c=state[k];
    50. if((a&b)||(a&c)||(b&c))continue;
    51. if(g[i]&c)continue;
    52. f[i&1][j][k]=max(f[i&1][j][k],f[i-1&1][u][j]+cnt[c]);
    53. }
    54. cout<1&1][0][0]<
    55. }

     愤怒的小鸟

    题目 愤怒的小鸟

    解析

    1. #include
    2. using namespace std;
    3. #define x first
    4. #define y second
    5. #define db double
    6. const int N=20,M=1<<18;
    7. const db eps=1e-6;
    8. typedef pair pdd;
    9. int f[M];
    10. pdd q[N];
    11. int n,m;
    12. int path[N][N];/
    13. bool cmp(db x,db y)//因为浮点数会失精度,用来判断两个数是否相等
    14. {
    15. if(fabs(x-y)return true;
    16. return false;
    17. }
    18. int main()
    19. {
    20. int T;
    21. cin>>T;
    22. while(T--)
    23. {
    24. cin>>n>>m;
    25. for(int i=0;i>q[i].x>>q[i].y;
    26. memset(path,0,sizeof path);//清空上一维的状态
    27. for(int i=0;i
    28. {
    29. path[i][i]=1<//自己与自己的抛物线能打自己,避免有时候只有一个点占一条抛物线
    30. for(int j=0;j
    31. {
    32. db x1=q[i].x,y1=q[i].y;
    33. db x2=q[j].x,y2=q[j].y;
    34. if(cmp(x1,x2)) continue;//判断相不相等,假如相等这条抛物线不存在
    35. db a=(y1/x1-y2/x2)/(x1-x2),b=y1/x1-a*x1;
    36. if(a>0||cmp(a,0.0)) continue;//开口向下,所以a<0
    37. for(int k=0;k//枚举这条抛物线能打到的其他点
    38. {
    39. db x=q[k].x,y=q[k].y;
    40. if(cmp(a*x*x+b*x,y)) path[i][j]+=1<//假如这个点在抛物线上,则加到这个ij点的状态下
    41. }
    42. }
    43. }
    44. memset(f,0x3f,sizeof f);//清空上一维的数,因为要最小值,所以初始化为正无穷
    45. f[0]=0;//这也是个合法的状态,初始化为0
    46. for(int i=0;i<1<//枚举每一个状态
    47. {
    48. int x=0;
    49. for(int j=0;j//找这个状态中没有猪的位置,也就是0的位置
    50. if(!(i>>j&1))
    51. {
    52. x=j;
    53. break;
    54. }
    55. for(int j=0;j//更新一下这个状态i与这只猪的状态下的最小值
    56. f[i|path[x][j]]=min(f[i|path[x][j]],f[i]+1);
    57. }
    58. cout<1<-1]<//输出所有猪打完的最小值,也就是所有位置都是1的状态
    59. }
    60. return 0;
    61. }

    宝藏----待更新

  • 相关阅读:
    使用基于swagger的knife4j自动生成接口文档
    【毕业设计】31-基于单片机的农业蔬菜大棚温度自动控制系统设计(原理图+源码+仿真工程+论文(低重复率))
    自定义maven骨架的添加与删除——完整详细介绍
    golang 并发同步(sync 库)-- 单例模式的实现(六)
    Word Power S
    SVM——支持向量机(一)
    仿牛客网项目---社区首页的开发实现
    机器人如何有效采摘苹果?
    什么是GIL锁,有什么作用?python的垃圾回收机制是什么样的?解释为什么计算密集型用多进程,io密集型用多线程。
    [ITIL]-ITIL4考点考题
  • 原文地址:https://blog.csdn.net/m0_64378422/article/details/127809484