牛客-模拟、枚举与贪心 2022.10.31

博客介绍了利用差分数组解决区间货物种类问题。先将相同编号货物种类区间排序,依据差分数组性质,对区间施加增量时修改差分数组,本题每次增量为1,最后通过前缀和得到每个位置不同货物的数目。

货物种类

差分数组。对于区间的货物种类,首先将所有相同编号的货物种类的区间,按照从小到大的顺序进行排列,其次按照差分数组的性质:

当我们希望对原数组的某一个区间[l,r]施加一个增量inc时,差分数组d对应的变化是:d[l]增加inc,d[r+1]减少inc,并且这种操作是可以叠加的。

当我们需要对原数组的不同区间施加不同的增量,我们只要按规则修改差分数组即可。

这道题目中,每次增量的数目为1。之后对于每个位置的不同货物的种数,进行一个前缀和,即可得到每个位置的不同货物的数目。

#include <bits/stdc++.h>
using namespace std;
#define M 100010
//维护一个差分数组存储各仓库货物种类数
int delta[M];

struct tag {
    int l, r, d;
}a[M];

//若货物编号相等,则按左端点升序排列,否则按编号升序排列
bool comp(tag x, tag y) {
    if(x.d == y.d)
        return x.l < y.l;
    return x.d < y.d;
}

int main() {
    int n, m, ans;
    cin >> n >> m;
    for(int i = 1; i <= m; i++)
        cin >> a[i].l >> a[i].r >> a[i].d;
    
    sort(a + 1, a + m + 1, comp);
    
    int temp_o = a[1].l, temp_d = a[1].r;
    for(int j = 2; j <= m; j++) {
        //出现新类型,差分数组端点变化
        //同时刷新起点和终点
        if(a[j].d != a[j-1].d) {
            delta[temp_o]++;
            delta[temp_d + 1]--;
            temp_o = a[j].l;
            temp_d = a[j].r;
        } else {
            if(a[j].l <= temp_d)
                temp_d = max(temp_d, a[j].r);
            //旧货物,进行区间合并或处理差分数组
            else {
                delta[temp_o]++;
                delta[temp_d + 1]--;
                temp_o = a[j].l;
                temp_d = a[j].r;
            }
        }
    }
    //最后一次没处理
    delta[temp_o]++;
    delta[temp_d + 1]--;
    
    //复原差分数组
    for(int k = 1; k <= n; k++)
        delta[k] += delta[k - 1];
    
    ans = 0;
    int a_max = 0;
    for(int s = 1; s <= n; s++) {
        //也可以逆序遍历,把等号改成大于或等于
        if(delta[s] > a_max) {
            ans = s;
            a_max = delta[ans];
        }
    }
    cout << ans;
    return 0;
}

(新学了差分数组这一数据结构,初次接触,之后好好复习)

网的算法入门班通常会围绕模拟枚举贪心这三种基础但重要的算法思想展开,尤其是在秋季训练营中,这类内容往往被安排在第一章或早期阶段,以便为新手打下扎实的基础。虽然无法直接提供2021秋季算法入门班的具体题目链接或完整题集,但可以结合常见的典型习题及解析方式来帮助理解这些算法的应用。 ### 模拟类题目 模拟类题目通常是根据题意进行一步步操作模拟,比如处理队列、栈的操作,或者按照某种规则进行状态更新的问题。 **示例题目:** - 题目描述:给定一个整数序列和一个操作次数 k,每次操作将数组中的每个元素替换为其后一个元素的值(最后一个元素变为第一个元素)。 - 解法思路:使用循环和数组拷贝即可实现,也可以通过数学推导优化成 O(n) 的时间复杂度。 - 示例代码: ```cpp #include <bits/stdc++.h> using namespace std; void rotateArray(vector<int>& nums, int k) { int n = nums.size(); vector<int> temp = nums; for (int i = 0; i < n; ++i) { nums[(i + k) % n] = temp[i]; } } int main() { vector<int> nums = {1, 2, 3, 4, 5}; int k = 2; rotateArray(nums, k); for (int num : nums) cout << num << " "; return 0; } ``` ### 枚举类题目 枚举类问题的核心是找出所有可能的情况,并从中筛选出满足条件的解。常见于子数组、子字符串、排列组合等问题。 **示例题目:** - 题目描述:给定一个整数数组,求出所有连续子数组的和,并找出最大值。 - 解法思路:暴力枚举所有子数组起点和终点,计算其和并维护最大值。 - 更优解法可使用 Kadane 算法实现 O(n) 时间复杂度[^1]。 - 示例代码(暴力枚举): ```cpp #include <bits/stdc++.h> using namespace std; int maxSubArraySum(int arr[], int n) { int max_sum = INT_MIN; for (int i = 0; i < n; ++i) { int current_sum = 0; for (int j = i; j < n; ++j) { current_sum += arr[j]; max_sum = max(max_sum, current_sum); } } return max_sum; } int main() { int arr[] = {-2, -1, -3, 4, -1, 2, 1, -5, 4}; int n = sizeof(arr) / sizeof(arr[0]); cout << "Maximum subarray sum is " << maxSubArraySum(arr, n) << endl; return 0; } ``` ### 贪心类题目 贪心算法的关键在于每一步都选择当前状态下最优的选择,希望最终结果全局最优。这类问题常用于活动选择、任务调度、硬币找零等场景。 **示例题目:** - 题目描述:给定一组物品的价值重量,以及背包的最大承重,求能装入背包的最大总价值(每种物品可取部分)。 - 解法思路:计算每个物品的单位重量价值,按从高到低排序,优先选取性价比高的物品。 - 示例代码: ```cpp #include <bits/stdc++.h> using namespace std; struct Item { int value, weight; double ratio; }; bool compare(Item a, Item b) { return a.ratio > b.ratio; } double fractionalKnapsack(int capacity, vector<Item>& items) { sort(items.begin(), items.end(), compare); double totalValue = 0.0; for (const auto& item : items) { if (capacity == 0) break; double take = min((double)item.weight, (double)capacity); totalValue += take * item.ratio; capacity -= take; } return totalValue; } int main() { vector<Item> items = {{60, 10}, {100, 20}, {120, 30}}; int capacity = 50; for (auto& item : items) item.ratio = (double)item.value / item.weight; cout << "Maximum value in Knapsack: " << fractionalKnapsack(capacity, items) << endl; return 0; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值