热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

状态压缩总结

本文主要是由Wiskey大神的博客的结合少许个人的总结,传送门概念:状态压缩是以二进制来保存每一个的状态,比如总共的物品有n件,那么我一共的状态有2^n次,最大的状态用二进制表示为11.

本文主要是由

Wiskey大神的博客的结合少许个人的总结,传送门

概念:
状态压缩是以二进制来保存每一个的状态,比如总共的物品有n件,那么我一共的状态有2^n次,最大的状态用二进制表示为11....n个1...11,经常得到这样的状态转移方程dp[11001] = dp[10001] + dp[11000] + dp[01001],当前状态只能由这些状态转移过来,相比较于一般的dp有什么优点呢?就在于二进制的位运算,一般的dp的话是用一个for循环来表示状态转移而二进制的速度快而且方便,注意位运算的优先级比较低,但是对于n比较大时,数组往往会开不下,所以一般n从1-20会选择使用状态压缩。

使用的范围:

根据二进制的特性,可以处理棋盘问题

1.简单棋盘问题

有n*n的一块棋盘,要往上面放n个棋子,同行同列不能有超过两个的棋子,问你有多少种方案

定义dp[i]表示状态为i的时候能放的种类数目,i其中的每一位表示的是列的编号,因为每一行只能放一个,所以状态转移的时候只要保证每一次都增加一个1就行了

这里有几个位运算的技巧

i)x&-x 可以得到x的从后往前的第一个非0的二进制 举个栗子 (注意二进制的位运算都是对于补码进行的)

x  原码 = 反码 = 补码 = 00101 

-x   原码 = 10101  反码 = 11010 补码 11011

x & -x = 00001

ii)i^x 可以得到i去掉这个x之后的数 假如i= 10110   x = 00100 那么异或一下就是 10010

这样就得到了前面一个状态,然后对于这个状态我们再让他进行与运算逐步得到所有的状态,直到这个i = 0

 代码:

#include
#include
#include
using namespace std;
const int maxn = 1 <<20;
int dp[maxn];
int main()
{
    int n;
    while(~scanf("%d",&n)){
        memset(dp,0,sizeof(dp));
        int i,tmp = (1 < 

2.对于有限制的棋盘问题(保证每一行都有一个能放)

#include
#include
#include
#include
const int maxn = 1 <<20;
int dp[maxn];
int map[20][20];
int a[20];
int n;
int main()
{
    while(~scanf("%d",&n)){
        for(int i = 1; i <= n ;i++){
            a[i] = 0;
            for(int j = 1; j <= n;j++){
                map[i][j] = rand()%2;
                a[i] = a[i]*2 + map[i][j];
                printf("%d",map[i][j]);
            }
            printf("\n");
        }
    int i,tmp = (1< 

3.给出一个n*m的棋盘(n,m <= 80),要在棋盘上放 k(k<=20) 个棋子,使得任意两个棋子不相临。问你方案种数。

定义dp[i][j][k]表示当前位于第i行时状态为s[j]棋子数目为k的种类数目,最后只要遍历所有可能的s[j]情况就是答案

状态转移方程  dp[i][j][k] += dp[i][p][k-c[j]](所有的p的可能性)只要满足 (s[j]&s[p]) == 0

#include
#include
#include
using namespace std;
const int maxn = 1 <<10;
int s[105],c[105],dp[15][105][10];
int n,m,pn;
int top;
void dfs(int t,int state,int count,int *flag)
{
    if(t == m){
        s[++top] = state;
        c[top] = count;
        return ;
    }
    flag[t+1] = 0;
    dfs(t+1,state*2,count,flag);
    if(flag[t] == 0){
        flag[t+1] = 1;
        dfs(t+1,state*2 + 1,count+1,flag);//flag表示当前是第几个为了保证flag[t] = 0,flag[t+1] = 1
    }
}
int main()
{
    while(~scanf("%d%d%d",&n,&m,&pn)){
        int flag[20] = {0};
        top = 0;
        if(n = c[j] + c[p])
                            dp[i][j][k] += dp[i-1][p][k-c[j]];
                    }
                }
            }
        }
    int sum = 0;
    for(int i = 1; i <= top; i++){
        sum += dp[n][i][pn];
    }
    printf("%d\n",sum);
}
return 0;
}

4.在n*n(n≤10)的棋盘上放k个国王(可攻击相邻的8个格子),求使它们无法互相攻击的方案数。

#include
#include
#include
using namespace std;
const int maxn = 1 <<10;
int s[105],c[105],dp[15][105][10];
int n,pn;
int top;
void dfs(int t,int state, int count,int *flag)
{
    if(t == n){
        s[++top] = state;
        c[top] = count;
        return ;
    }
    flag[t] = 0;
    dfs(t+1,state*2,count,flag);
    if(flag[t-1] == 0 || t == 0){
        flag[t] = 1;
        dfs(t+1,state*2+1,count+1,flag);
    }
}
int main()
{
    while(~scanf("%d%d",&n,&pn)){
        int flag[20] ={0};
        top = 0;
        dfs(0,0,0,flag);
        //printf("%d\n",top);
        memset(dp,0,sizeof(dp));
        dp[0][1][0] = 1;
        for(int i = 1; i <= n ;i++){
            for(int j = 1; j <= top ;j++){
                for(int p = 1; p <= top; p++){
                    for(int k = c[j]; k <= pn; k++){
                        if((s[p]&s[j])||(s[j]&s[p]<<1)||(s[j]&s[p]>>1))
                            continue;
                            if(k >= (c[p] + c[j]))
                            dp[i][j][k] += dp[i-1][p][k-c[j]];
                    }
                }
            }
        }
        int sum = 0;
        for(int i = 1; i <= top;i++)
          sum +=  dp[n][i][pn] ;
        printf("%d\n",sum);
    }
    return 0;
}

  

 


推荐阅读
  • 本题探讨了在一个有向图中,如何根据特定规则将城市划分为若干个区域,使得每个区域内的城市之间能够相互到达,并且划分的区域数量最少。题目提供了时间限制和内存限制,要求在给定的城市和道路信息下,计算出最少需要划分的区域数量。 ... [详细]
  • 本文探讨了在C++中如何有效地清空输入缓冲区,确保程序只处理最近的输入并丢弃多余的输入。我们将介绍一种不阻塞的方法,并提供一个具体的实现方案。 ... [详细]
  • 本文详细介绍了C++中map容器的多种删除和交换操作,包括clear、erase、swap、extract和merge方法,并提供了完整的代码示例。 ... [详细]
  • 丽江客栈选择问题
    本文介绍了一道经典的算法题,题目涉及在丽江河边的n家特色客栈中选择住宿方案。两位游客希望住在色调相同的两家客栈,并在晚上选择一家最低消费不超过p元的咖啡店小聚。我们将详细探讨如何计算满足条件的住宿方案总数。 ... [详细]
  • 在进行QT交叉编译时,可能会遇到与目标架构不匹配的宏定义问题。例如,当为ARM或MIPS架构编译时,需要确保使用正确的宏(如QT_ARCH_ARM或QT_ARCH_MIPS),而不是默认的QT_ARCH_I386。本文将详细介绍如何正确配置编译环境以避免此类错误。 ... [详细]
  • JSOI2010 蔬菜庆典:树结构中的无限大权值问题
    本文探讨了 JSOI2010 的蔬菜庆典问题,主要关注如何处理非根非叶子节点的无限大权值情况。通过分析根节点及其子树的特性,提出了有效的解决方案,并详细解释了算法的实现过程。 ... [详细]
  • Qt QTableView 内嵌控件的实现方法
    本文详细介绍了在 Qt QTableView 中嵌入控件的多种方法,包括使用 QItemDelegate、setIndexWidget 和 setIndexWidget 结合布局管理器。每种方法都有其适用场景和优缺点。 ... [详细]
  • 深入解析Java枚举及其高级特性
    本文详细介绍了Java枚举的概念、语法、使用规则和应用场景,并探讨了其在实际编程中的高级应用。所有相关内容已收录于GitHub仓库[JavaLearningmanual](https://github.com/Ziphtracks/JavaLearningmanual),欢迎Star并持续关注。 ... [详细]
  • 本题来自WC2014,题目编号为BZOJ3435、洛谷P3920和UOJ55。该问题描述了一棵不断生长的带权树及其节点上小精灵之间的友谊关系,要求实时计算每次新增节点后树上所有可能的朋友对数。 ... [详细]
  • 树链问题的优化解法:深度优先搜索与质因数分解
    本文介绍了一种通过深度优先搜索(DFS)和质因数分解来解决最长树链问题的方法。我们通过枚举树链上的最大公约数(GCD),将所有节点按其质因子分类,并计算每个类别的最长链,最终求得全局最长链。 ... [详细]
  • 本文详细介绍了如何在C#程序运行期间防止系统进入休眠模式以及显示器关闭,提供了具体的实现代码示例,并解释了其应用场景。这不仅有助于提高程序的稳定性,还能优化能源管理。适合需要处理长时间任务(如下载或批处理)的开发者参考。 ... [详细]
  • 题目描述:给定一个N*M的网格,初始时网格中有k个芯片,每个芯片的位置已知。玩家可以在每一步操作中将所有芯片沿同一方向移动一格。如果芯片到达边界,则保持不动。目标是通过一系列操作,使每个芯片依次访问指定的目标位置。 ... [详细]
  • 本题要求实现一个函数,用于检查给定的字符串是否为回文。回文是指正向和反向读取都相同的字符串。例如,“XYZYX”和“xyzzyx”都是回文。 ... [详细]
  • 实用正则表达式有哪些
    小编给大家分享一下实用正则表达式有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下 ... [详细]
  • 本文探讨了如何通过预处理器开关选择不同的类实现,并解决在特定情况下遇到的链接器错误。 ... [详细]
author-avatar
mobiledu2502860983
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有