左神:中级提升班1

1.窗口不回退

        1.1最多覆盖点

2.打表法

        2.1买苹果

        2.2先后手吃草

3.预处理

        3.1染色

        3.2最大正方形边长

        3.3加工函数


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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jomo.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值