1.窗口不回退
1.1最多覆盖点
方法一:贪心
思路:
1.把绳子的右端依次放在每个点上,记录所覆盖过的最多点数
2.确定绳子右端后,找大于等于绳子左端的最左位置
//返回大于等于value的最左的值对应的索引
int nearestIndex(vector<int>& vec, int R, int value) {
int left = 0;
int index = R;
while (left < R) {
int mid = left + ((R - left) >> 1);
if (vec[mid] >= value) {
index = mid;
R = mid - 1;
}
else {
left = mid + 1;
}
}
return index;
}
int maxPoint1(vector<int>& vec, int L) {
int res = 1;
for (int i = 0; i < vec.size(); i++) {
int nearest = nearestIndex(vec, vec[i], vec[i] - L);
res = max(res, i - nearest + 1);//索引差+1即为覆盖点数
}
return res;
}
时间复杂度: O(N*logN)
方法二:滑动窗口
思路:把绳子的左端依次放在每个点上,维持一个窗口,记录所覆盖过的最多点数
int maxPoint2(vector<int>& vec, int L) {
int res = 1;
int R = 0;
//R没必要回退
for (int i = 0; i < vec.size(); i++) {
while (vec[R] - vec[i] <= L)R++;
res = max(res, R - i + 1);
}
return res;
}
时间复杂度: O(N),因为左边界右边界都不需要回退
2.打表法
2.1买苹果
思路:
需要n个苹果,尽量都买8个每袋的可以保证袋数最小,然后余下的苹果用6个每袋的解决。但是,当余下的苹果大于24时,
就不需要再试了。假设余下27个,27=24+3;再递减,余下35个,35=24+11;余下43个,
43=24+19。都是前面的基础类型出现过的,因为24是6和8的最小公倍数。24的那部分可以用8搞定,用6搞定袋数更多,不符合题意要求。
int minBags1(int apple) {
if (apple < 0)return -1;
int bag6 = -1;
int bag8 = apple / 8;
int rest = apple - 8 * bag8;
while (bag8 >= 0 && rest < 24) {
int bagUse6 = rest % 6 == 0 ? (rest / 6) : -1;
if (bagUse6 != -1) {
bag6 = bagUse6;
break;
}
rest = apple - 8 * (--bag8);
}
return bag6 == -1 ? -1 : bag6 + bag8;
}
总结规律后:打表法
int minBagAwesome(int apple){
if((apple&1)!=0)return -1;
if(apple<18){
return apple==0?0:(apple==6||apple==8)?1:(apple==12||apple==14||appple==16)?2:-1;
}
return (apple-18)/8+3;
}
2.2先后手吃草
问题:
两只羊每次只能选择吃4的幂次方草,给定n草,在先后手顺序吃草的情况下,谁刚好把草吃光就赢
string winner1(int n) {
//0 1 2 3 4
//后 先 后 先 先
if (n < 5) {
return (n == 0 || n == 2) ? "后手" : "先手";
}
//n>=5时
int base = 1;//先手决定吃的草
while (base <= n) {//试吃1/4/16……份草,看各个结果,有一个能赢就行。
//当前先手吃掉base份草,留下n-base份草给后手
//母过程的“先手”是子过程的“后手”
if (winner1(n - base) == "后手")return "先手";
if (base>n/4)break;//防止base*4后溢出,即防止base大于INT_MAX
base *= 4;
}
return "后手";
}
总结规律后:打表法
string winner2(int n){
if(n%5==0||n%5==2)return "先手";
return "后手";
}
3.预处理
3.1染色
int minPaint(string s) {
if (s.length() == 0 || s.length() == 1)return 0;
int len = s.length();
vector<int>leftG(len);//记录左半部分有多少G
vector<int>rightR(len);//记录右半部分有多少R
leftG[0] = s[0] == 'G' ? 1 : 0;
rightR[0] = s[len - 1] == 'R' ? 1 : 0;
for (int i = 1; i < len - 1; i++) {
leftG[i] = leftG[i - 1] + (s[i] == 'G' ? 1 : 0);
rightR[len - 1 - i] = rightR[len - i] + (s[len - 1 - i] == 'R' ? 1 : 0);
}
//注意边界,因为循环时i不达到边界
leftG[len - 1] = leftG[len - 2] + (s[len - 1] == 'G' ? 1 : 0);
rightR[0] = rightR[1] + (s[0] == 'R' ? 1 : 0);
int res = rightR[0];
for (int j = 0; j < len; j++) {
res = min(res, leftG[j] + rightR[j]);//不会重复包含,因为s[j]要么是R要么是G
}
return res;
}
3.2最大正方形边长
长方形子矩阵数:(N^4)/2,在总矩阵找两个点,可能性各为n^2,两点可确定一个矩阵(对角线),最多重复一次。
正方形子矩阵数:(N^3)/2,n^2*n=n^3,因为第二个点有特殊要求
创建辅助结构:
1.right:记录二维数组每行中每个数右边(包含自己)有多少个连续的1,如果自身为0,那么直接就是0个
2.down:每个数下方有多少个连续的1,逻辑和right类似
void setBorderMap(vector<vector<int>>& m, vector<vector<int>>& right, vector<vector<int>>& down) {
int r = m.size();
int c = m[0].size();
if (m[r - 1][c - 1] == 1) {
right[r - 1][c - 1] = 1;
down[r - 1][c - 1] = 1;
}
//处理右边
for (int i = r - 2; i != -1; i--) {
if (m[i][c - 1] == 1) {
right[i][c - 1] = 1;
down[i][c - 1] = down[i + 1][c - 1] + 1;
}
}
//处理下边
for (int i = c - 2; i != -1; i--) {
if (m[r - 1][i] == 1) {
right[r - 1][i] = right[r - 1][i + 1] + 1;
down[r - 1][i] = 1;
}
}
//处理中间
for (int i = r - 2; i != -1; i--) {
for (int j = c - 2; j != -1; j--) {
if (m[i][j] == 1) {
right[i][j] = right[i][j + 1] + 1;
down[i][j] = down[i + 1][j] + 1;
}
}
}
}
bool hasSizeOfBorder(int size, vector<vector<int>>& right, vector<vector<int>>& down) {
for (int i = 0; i != right.size() - size + 1; i++) {
for (int j = 0; j != right[0].size() - size + 1; j++) {
if (right[i][j] >= size && down[i][j] >= size && right[i + size - 1][j] >= size && down[i][j + size - 1] >= size) {
return true;
}
}
}
return false;
}
int getMaxSize(vector<vector<int>>& m) {
vector<vector<int>> right(m.size(),vector<int>(m[0].size()));
vector<vector<int>> down(m.size(), vector<int>(m[0].size()));
setBorderMap(m, right, down);
for (int size = min(m.size(), m[0].size()); size != 0; size--) {
if (hasSizeOfBorder(size, right, down)) {
return size;
}
}
return 0;
}
3.3加工函数
//等概率返回0和1的函数
int r01(){
int res=0;
do{
res=f();
}while(res==3);
return res<3?0:1; //等分数字,小于的范围返回0,;大于的范围返回1;多余的那一个数重做。
}
//1~7
int g(){
int res=0;
do{
res=(r01()<<2)+(r01()<<1)+r01();
}while(res==7);//大于所需范围的重做
return res+1;//因为res是从0开始的,所以加上c之后就属于需要等概率返回的范围了
}
//调用两次f,如果是01返回0,如果是10就返回1,其他重做。因为这两种情况等概率,都是p(1-p)
int g(){
int res=0;
do{
res=(f()<<1)+f();
}while(res==0||res==3);
return res-1;
}