暴力枚举
坑
- 多次使用vis记得清空
思路与总结
-
二分可以优化很多
-
使用dfs进行暴力搜索要知道两次递归的关系,以及递归一次的时候进行的操作,合理的设置参数来达到某些目的
-
要能够识别暴力搜索,是有些东西看起来有规律,但是实际上不用讨论规律,直接暴力就能够实现,这种做法往往会被忽略
-
暴力不代表蛮力,也有优化的地方,有时候要防止枚举重复的
- 有的时候需要考虑换一种枚举方式暴力反而更简单,更容易进行枚举
- 暴力也可以通过设置合适的顺序来减少枚举的次数
-
二进制枚举的几个技巧
//枚举0-2^n - 1 int len; for(int i = 0; i <(1 << len); i ++){ //1<<len 即为 2^n } //取每一位为1进行对于操作 for(int i = 0; i < len; i ++){ if(num & (1 < i)){//num为长度为len的数字,1<i进行移位操作,然后使用&就可以获得对应位的二进制了 } }
作业
-
1108
-
打表预处理
-
使用unique优化结果集合
-
二分法查找顺序集合
sort(arr, arr + n); int size = unique(arr, arr + n) - arr;//unique返回的是排序后多余的下标 //如 1 2 2 3 4 4 => 1 2 3 2 4 会返回指向第二个2的指针,因此相剪就会变成排序后的长度,再重新利用arr就可以使用去重后的数列
-
-
1063
-
埃式打表法求素数,然后判断回文
-
利用预处理前缀和来快速查找区间的个数
//打表法求素数 bool p[N];//false为素数 void init(){ memset(p, 0, sizeof(p)); p[1] = [1];//1不是素数 for(int i = 2; i < N; i ++){ if(! p[i]){ for(long long j = 1ll * i * i; j < N; j += i){//1.使用longlong,2.递增是+i,不是+1 p[j] = true; } } } } //使用前缀和 int pre[N]; for(int i = 1; i < N; i ++){ if(! p[i] && check(i)) sum[i] = sum[i - 1] + 1; else sum[i] = sum[i - 1]; }
-
题号
-
PIPIOJ 1130: 奇偶交错排列
-
PIPIOJ 1133: 棋盘问题
-
PIPIOJ 1138: N皇后问题
-
PIPIOJ 1102: PIPI学加法
-
PIPIOJ 1049: PIPI的按钮Ⅰ
-
PIPIOJ 1066: 竖式问题
-
PIPIOJ 1322: 同心共筑中国梦
-
PIPIOJ 1084: 最长公共子序列Ⅱ
-
PIPIOJ 1168: PIPI的方格
分析
- 1130
- 预处理出所有的和,然后使用二分进行查找
- 1133
- 这里对枚举有进行优化,为了防止枚举重复的,可以在参数上设置的更加有技巧,这题我参数设置的是选中一个点后,之后枚举只会枚举后面的点,其实更好的是用行来进行操作
- 1138
- 很经典的n皇后问题,只是这里vis数组可以扩展到主副对角线
- 1102
- 有个去重的技巧,就是在dfs回溯的时候,去除掉相同的数字,防止重复枚举
- 1049
- 其次我一开始想的是两层dfs,这样想不太对,因为针对ans答案输出数组来说,两层dfs都是对这个进行操作,那么应该理解成为一层才对,通过设置参数start,来限制枚举开始的起点,从而让dfs有不同的意义
- 1084
- 求最长公共子序列,没有想出来,这里给出答案是使用二进制进行模拟,暴力枚举出所有的子序列,使用hashmap保存后,再之后的字符,每次枚举子序列,看看map查询的到么,如果查到了就保存到临时map,之后使用临时map去更新hashmap,来使得每次求出的子序列得到更新,逐渐缩短
- 对于多个可能的结果,需要去字典序最小但是又要长度最长,就在最后一次循环中,对hashmap中的子序列进行长度判断,如果长度相同进行字典序判断
- 环状字符处理的技巧,s+=s;扩展成为双倍
- 1168
- 如果直接暴力枚举方阵的状态,不合适,考虑到第一行确定,而剩下的就会确定,因此可以直接使用二进制枚举第一行,然后对第一行进行判断是否合题意(要注意的是最后一行还要判断一次,因为之前的循环只是生成最后一行,最后一行的合法性需要额外拿出来判断)