作业介绍

2026年编程兔冬令营集训第四场作业

1、位运算

2、二分

1、计算10进制数转换成2进制数后1的个数。
#include<bits/stdc++.h>
using namespace std;
int main(){
	long long n,ans =0;
	cin>>n;
	while(n>0){
		ans+=n%2;
		n=n/2;
	}
	cout<<ans<<endl;
	//cout<<__builtin_popcount(int n)<<endl;
	return 0;
} 

2、枚举进阶:全排列问题。问题:枚举 n 个不同元素的所有全排列(每个元素出现且仅出现一次的排列)。n 个元素的全排列共有 n! 种,时间复杂度为 O (n!)。该方法适用于 n≤10 的场景(10!≈360 万,可接受)。
思路:
• 递归枚举:固定第 k 个位置的元素,递归枚举剩余位置的元素。
• 去重处理:通过交换元素避免重复(确保每个位置的元素唯一)。
#include<bits/stdc++.h>
using namespace std;
int a[20],n;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n;
	for(int i = 1;i <= n;i++) a[i] = i;
	do{
		for(int i = 1;i <= n;i++) cout << a[i] << ' ';
		cout << "\n";
	}while(next_permutation(a + 1,a + n + 1));
	return 0;
}

#include<bits/stdc++.h>
using namespace std;
int a[11],b[11],n;
void dfs(int x)
{
	if(x==n+1)
	{
		for(int i=1;i<=n;i++)cout<<a[i]<<" ";
		cout<<endl;
		return ;
	}
	for(int i=1;i<=n;i++)
	{
		if(b[i]==0)
		{
			a[x]=i;
			b[i]=1;
			dfs2(x+1);
			b[i]=0;
		}
	}
}
int main()
{
	cin>>n;
	dfs(1);
	return 0;
}


3、题意:给 n 个动物和一个条件列表:如果存在一个动物第 i 位为 1,则购买第 i 位的饲料,否则不购买第 i 位饲料。根据当前的动物,确定了饲料集合。问在不改变饲料集合的前提下,还能加入多少个动物。重点就是二进制位数对应饲料。本题数据会超过64位,可以使用__int128类型。
a.如果某一位二进制没有出现过对应的饲料,则这一位二进制可0可1,不会增加新饲料;
b.如果某一位二进制出现过饲料,并且这一位二进制在动物中出现过,饲料必买,则这一位二进制可0可1,不会增加新饲料。
#include<bits/stdc++.h>
using namespace std;
int b[65];
void show(__int128 ans){
	string s;
	if (ans ==0) {
		cout<<0<<endl;return;
	}
	while(ans>0){
		s.push_back('0'+ans%10);
		ans/=10;
	}
	reverse(s.begin(), s.end());
	cout<<s<<endl;
	return;
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	unsigned long long n,m,c,k,p,q, tn=0, ai,one=1;
	__int128 ans=1; //总方案会超过64位 
	cin>>n>>m>>c>>k;
	//统计动物对应的位数为1
	for(int i=0;i<n;++i) {
		cin>>ai;
		tn=tn|ai;
	}
	//统计饲料对应的位数为1 
	for(int i=0;i<m;++i){
		cin>>p>>q;
		b[p] = 1;
	}
	//枚举位数,计算总方案 
	for(int i=0;i<k;++i){
		if (b[i]==0){//没有对应的饲料出现,可0可1,方案为2 
			ans = ans*2;
		}else{
			// 有对应的饲料,且已经购买,可0可1,方案为2 
			if ((tn & (one<<i)) !=0 ) ans = ans *2;
		}
	}
	// 总方案-已经出现的=剩余的 
	ans = ans -n;
	show(ans);
	return 0;
}
4、二分查找,它仅适用于有序的顺序表。
折半查找的基本思想:① 首先将给定值 key 与表中中间位置的元素比较,若相等,则查找成功,返回该元素的存储位置;② 若不等,则所需查找的元素只能在中间元素以外的前半部分或后半部分(例如,在查找表升序排列时,若 key 大于中间元素,则所查找的元素只可能在后半部分),然后在缩小的范围内继续进行同样的查找。重复上述步骤,直到找到为止,或确定表中没有所需要查找的元素,则查找不成功,返回查找失败的信息。
#include<bits/stdc++.h>
using namespace std;
int n,q;
int a[100005];
bool query(int x){
	int l=0,r=n-1,mid;
	while(l<=r){
		mid = (l+r+1)/2;
		if (a[mid] == x) return x;
		if (a[mid]<x) l=mid+1;
		else r=mid-1;
	}
	
	return false;
}
int main(){
	cin>>n>>q;
	for(int i=0;i<n;++i) cin>>a[i];
	//排序,确保有序 
	sort(a,a+n);
	int x;
	while(q-->0){
		cin>>x;
		cout<<(query(x)?"Yes":"No")<<endl;
	}
	return 0;
}
使用lower_bound函数
#include<bits/stdc++.h>
using namespace std;
int n,q;
int a[100005];
bool query(int x){
	int mid = lower_bound(a,a+n,x)-a; 
	if (mid<n && a[mid]==x) return true;
	
	return false;
}
int main(){
	cin>>n>>q;
	for(int i=0;i<n;++i) cin>>a[i];
	//排序,确保有序 
	sort(a,a+n);
	int x;
	while(q-->0){
		cin>>x;
		cout<<(query(x)?"Yes":"No")<<endl;
	}
	return 0;
}

5、题目要求将数列划分成  段,求“每段和的最大值”的最小值。每当我们看到 “最大值最小” 或 “最小值最大” 这类字眼时,首先应该想到的就是 二分答案。
a.二分枚举“每段和的最大值”这一可能得答案(设为 x)。 
b.然后检查:如果每段和最大不超过 x,最少需要分成几段?
#include<bits/stdc++.h>
using namespace std;
// 使用 long long (ll) 是为了防止和溢出。
// 虽然 N <= 10^5, A_i <= 10^8,但总和可能达到 10^13,超过 int (2*10^9) 范围。
typedef long long ll;
const int MAXN = 100005;
ll a[MAXN];
/**
 * 检查函数 check(limit)
 * 功能:判断当每段和的最大值不超过 limit 时,最少需要分多少段。
 *
 * 贪心策略:
 * 尽可能把更多的数字塞进当前这一段,直到塞不下(和超过 limit)为止,
 * 然后开启新的一段。这样得到的段数是最少的。
 */
bool check(int n, int m, ll limit) {
    int seg = 1;     // 当前是第几段(初始为第1段)
    ll cur_sum = 0;  // 当前这一段的累加和
    for (int i = 1; i <= n; i++) {
        // 如果加上当前数字 a[i] 会超过限制 limit
        if (cur_sum + a[i] > limit) {
            seg++;           // 必须开启新的一段
            cur_sum = a[i];  // 新的一段从 a[i] 开始
        } else {
            cur_sum += a[i];  // 否则加到当前段里
        }
    }
    // 如果最少需要的段数 seg <= m,说明 limit 是可行的(甚至可能偏大),返回
    // true
    return seg <= m;
}
int main() {
    int N, M;
    cin >> N >> M;
    ll sum = 0;
    ll l = 0;  // 二分下界:答案至少是数列中的最大值(因为一段至少包含一个数)
    for (int i = 1; i <= N; i++) {
        cin >> a[i];
        sum += a[i];
        l = max(l, a[i]);
    }
    // 二分上界:答案至多是所有数的和(即只分成 1 段)
    ll r = sum;
    // 二分查找
    while (l <= r) {
        ll mid = l + (r - l) / 2;
        if (check(N, M, mid)) {
            // 如果 limit = mid 可行(分的段数 <= M),说明答案可能是 mid
            // 或者更小
            r = mid - 1;
        } else {
            // 如果不可行(分的段数 > M),说明 limit 太小了,需要增大
            l = mid + 1;
        }
    }
    // 最终 l 指向的就是满足条件的最小值
    cout << l << endl;
    return 0;
}

6、思路:二分答案 + 差分数组
a.二分订单数k,判断前k个订单是否导致某天教室不足。
b.用差分数组快速计算每天教室使用量,复杂度O(n + m log m)。
#include<bits/stdc++.h>
using namespace std;

// 检查函数,用于判断在给定的订单索引mid之前,是否所有订单都能被满足
bool check(const vector<long long>& rooms, const vector<vector<int>>& orders, int mid) {
    vector<long long> diff(rooms.size() + 1, 0); // 初始化差分数组
    for (int i = 0; i <= mid; ++i) { // 遍历到mid的订单
        diff[orders[i][1]] += orders[i][0]; // 在订单开始日增加需求
        diff[orders[i][2] + 1] -= orders[i][0]; // 在订单结束日的下一天减少需求
    }
    long long current = 0; // 当前已分配的教室数量
    for (int i = 1; i < rooms.size(); ++i) { // 遍历每一天
        current += diff[i]; // 更新当前已分配的教室数量
        if (current > rooms[i]) { // 如果当前需求超过可用教室
            return false; // 返回false,表示无法满足
        }
    }
    return true; // 如果所有天都能满足,返回true
}

int main() {
    int n, m;
    cin >> n >> m; // 读取天数n和订单数量m
    vector<long long> rooms(n + 1, 0); // 初始化教室数组,0号位置不用
    for (int i = 1; i <= n; ++i) {
        cin >> rooms[i]; // 读取每天的教室数量
    }
    vector<vector<int>> orders(m, vector<int>(3)); // 初始化订单数组
    for (int i = 0; i < m; ++i) {
        cin >> orders[i][0] >> orders[i][1] >> orders[i][2]; // 读取每个订单的需求、开始和结束日期
    }

    // 二分查找第一个无法满足的订单
    int left = 0, right = m - 1; // 初始化二分查找的左右边界
    while (left <= right) {
        int mid = left + (right - left) / 2; // 计算中间索引
        if (check(rooms, orders, mid)) { // 如果mid之前的订单都能满足
            left = mid + 1; // 向右移动左边界
        } else {
            right = mid - 1; // 否则向左移动右边界
        }
    }

    if (left == m) {
        cout << 0 << endl; // 如果left等于m,表示所有订单都能满足
    } else {
        cout << -1 << endl; // 否则输出-1
        cout << left + 1 << endl; // 输出第一个无法满足的订单的编号(编号从1开始)
    }

    return 0;
}


题目

认领作业后才可以查看作业内容。
状态
正在进行…
题目
7
开始时间
2026-2-4 0:00
截止时间
2027-1-1 23:59
可延期
24 小时