NOIP 2005年提高组初赛题

完善程序1,木材加工
#include <stdio.h>
int n, k, len[10000];

int isok(int t) {
	int num = 0, i;
	for (i = 0; i < n; i ++) {
		if (num >= k) break;
		num = num + len[i] / t;
	}
	if (num >= k) return 1;
	else return 0;
}

int main() {
	int i, left, right, mid;
	scanf("%d%d", &n, &k);
	right = 0;
	for (i = 0; i < n; i ++) {
		scanf("%d", &len[i]);
		if (right < len[i]) right = len[i];
	}
	
	right ++;		//左闭右开区间 [left, right) 
	left = 0;		//如果切不出来则输出0,所以left初始值为0
	
	//通常模板是左闭右闭区间,循环条件写left < right
	//这里写的是左闭右开区间,循环条件应该写成left + 1 < right
	//这么处理的目的是:left和right不可能是两个相邻的整数,
	//那么mid不可能和left、right相同,所以就不会出现死循环 
	while (left + 1 < right) {
		mid = (left + right) / 2;
		if (isok(mid) == 0) right = mid;
		else left = mid;
		//注意:由于函数返回值为1的时候,left更新为mid,
		//这样一来,left每次取的值都是ok的,最后一次赋值就是最大长度	
		//当我们要找符合条件的数的最大值时,应该用left,从小到大逼近最大值	
	}
	printf("%d\n", left);

	return 0;
}

/*
输入: 
8 1
3 1 4 1 5 9 2 6
输出:
9 

输入: 
8 1000
3 1 4 1 5 9 2 6
输出:
0 
*/
找出历年初赛题中出现的二分代码,进行对比,总结常用的几个模板
2015年普及组,完善程序2:中位数
//这里是左闭右闭区间[left, right]
while (left < right) {
	mid = (left + right) / 2;
	
	//函数返回“真”时,正确答案在[mid + 1, right]区间内
	//返回“假”时,正确答案在[left, mid]区间内
	if (check(mid)) 	
		left = mid + 1;
	else 				
		right = mid;
}
cout << right;
2017年普及组,完善程序2:切割绳子
//这里是左闭右闭区间[left, right]
while (left < right) {
	//注意:计算mid时,右边的表达式必须 + 1,
	//否则当执行left = mid语句时,left的值没发生变化,陷入死循环
	mid = (left + right + 1) / 2;
	
	//函数返回“真”时,正确答案在[left, mid - 1]区间内
	//返回“假”时,正确答案在[mid, right]区间内
	if (check(mid)) 	
		right = mid - 1;
	else 				
		left = mid;
}
cout << left;
2016年普及组,完善程序2:郊游活动
//这里是左闭右闭区间[left, right]
//循环条件必须写成left <= right
while (left <= right) {
	//注意:计算mid时,右边的表达式不需要 + 1,
	mid = (left + right) / 2;
	
	//函数返回“真”时,正确答案可能是mid,所以将mid先存入ans,
	//也可能在[mid + 1, right]区间内,所以继续查找右区间
	//返回“假”时,正确答案不可能是mid,在[left, mid - 1]区间内
	if (check(mid)) {
		ans = mid;
		left = mid + 1;
	}
	else {				
		right = mid - 1;
	}
}
//变量ans的值一直是候选答案,最后一次循环时更新的一定是正确答案
cout << ans;
更推荐使用最后一个模板,
  • 更新mid时一定是(left + right) / 2
  • 更新left时一定是mid + 1
  • 更新right时一定是mid - 1
  • 不用担心死循环的情况
  • 要注意的是:while循环的条件是left <= right
完善程序2:N叉树
#include <stdio.h>
#include <string.h>
using namespace std;
char str1[100], str2[100];
int N;
long com[100][100];

//返回x选y的组合数 
//combination:组合 
long getcom(int x, int y) {
	if (y == 0 || x == y) return 1;
	else if (com[x][y] != 0) return com[x][y];
	else {
		com[x][y] = getcom(x - 1, y) + getcom(x - 1, y - 1);
		return com[x][y];
	}
}

//返回当前递归的这棵树不同形态的数目 
//参数:当前递归的这棵树在str1里的起点,终点;在str2里的起点 
long count(int a, int b, int c) {
	long sum = 1;	
	int k = 0;				//统计当前递归的这棵树的子树的数量 
	int s = a + 1, t = c, p;//s:当前递归的这棵树的子树在str1里的起点 
	if (a == b) return 1;	//如果只有一个字符,则只有一个形态	 
	while (s <= b) {		//循环枚举各棵子树 
		p = t;				//p: 当前递归的这棵树的子树在str2里的起点 
		while (str1[s] != str2[t]) t ++;//t: 当前递归的这棵树的子树在str2里的终点 
		
		//(因为前序遍历的第一个字符和后序遍历的最后一个字符都是根结点,
		//这样我们就确定了子树字符串的长度)
		sum = sum * count(s, s + t - p, p);

		s = s + t - p + 1;	//当前递归的这棵树的下一个子树在str1里的起点 
		t ++;				//当前递归的这颗树的下一个子树在str2里的起点 
		k ++;				//当前递归的这棵树的子树的数量加一 
	}
	return sum * getcom(N, k);
}

int main() {
	int len;
	scanf("%d", &N);
	scanf("%s%s", str1, str2);
	len = strlen(str1);
	printf("%ld\n", count(0, len - 1, 0));

	return 0;
}

/*
输入: 
4
abdefgc
defgbca

输出:
6 
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值