P3817 小A的糖果

记录48

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,x,last=0,now;
  long long cnt=0;
	cin>>n>>x;
	while(n--){
		cin>>now;
		if(now+last>x){
			cnt+=now+last-x;
			now=x-last;
		} 	
		last=now;
	}
	cout<<cnt;
	return 0;
} 

题目传送门https://www.luogu.com.cn/problem/P3817


突破点

小 A 有 n 个糖果盒,第 i 个盒中有 ai​ 颗糖果

小 A 每次可以从其中一盒糖果中吃掉一颗,他想知道,要让任意两个相邻的盒子中糖的个数之和都不大于 x,至少得吃掉几颗糖。

👉两个数绑定处理


思路

要不大于的x的话,从一开始就要吃掉超过x的部分,后面就一直吃掉多出来的部分

  1. 让当前的盒子糖数跟前一个盒子糖数对比
  2. 大于X,吃掉当前盒子中多出X的部分
  3. 循环重复上述操作

代码简析

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,x,last=0,now;
  long long cnt=0;
	cin>>n>>x;
	while(n--){
		cin>>now;
		if(now+last>x){
			cnt+=now+last-x;
			now=x-last;
		} 	
		last=now;
	}
	cout<<cnt;
	return 0;
} 

 int n(多少个盒子),x(不可超过),last=0(上一个盒子糖数),now(当前盒子糖数);

注意:吃第一个盒子糖因为前面没有其他盒子,所以为0

 long long cnt=0;👉计算一共吃了多少个

注意:数据范围(对于 100% 的数据,保证 2≤n≤10的五次方,0≤ai​,x≤10的九次方),数字很大了,开long long

while(n--){}👉循环盒子糖数

cin>>now;👉输入当前盒子糖数

if(now+last>x){}👉处理超过部分

cnt+=now+last-x;👉吃掉多出来的部分

now=x-last;👉吃掉的是当前盒子的

last=now;👉新人变旧人,新盒变上一个盒子


补充

CSP-J 贪心问题技巧完全汇总


一、贪心问题的识别特征(拿到题先判断)

高频关键词

  • "最少/最多" :选最少的点覆盖所有区间、最多能选几个

  • "最短/最长" :最短时间完成、最长上升子序列(贪心+二分)

  • "最大/最小" :最大收益、最小代价

  • "最优策略" :每次选当前最好的

  • "资源有限" :预算、时间、人数限制

CSP-J中的典型题型

✓ 活动安排/区间调度(最多选几个不重叠活动)
✓ 背包类(选物品不超过容量)
✓ 装水/接雨水(平铺问题)
✓ 排队/等待(最小化等待时间)
✓ 字符串构造(字典序最小)

二、五大核心贪心策略(背诵模板)

策略1:区间调度(CSP-J考频★★★★★)

问题:N个活动,每个有开始时间s和结束时间e,选最多活动,互不重叠。

模板

struct Activity {
    int start, end;
};

bool cmp(const Activity &a, const Activity &b) {
    return a.end < b.end;  // 按结束时间从小到大
}

// 步骤:
// 1. 按结束时间排序
// 2. 维护当前最后结束时间last_end
// 3. 若当前start >= last_end,选上,更新last_end

int greedy(vector<Activity> &acts) {
    sort(acts.begin(), acts.end(), cmp);
    int cnt = 0, last_end = -1;
    for (auto &act : acts) {
        if (act.start >= last_end) {
            cnt++;
            last_end = act.end;
        }
    }
    return cnt;
}

证明:结束越早,留给后面空间越多,反证法易证最优。


策略2:背包贪心(分数背包)

问题:背包容量W,N个物品有价值v和重量w,可分割,求最大价值。

模板

struct Item {
    int v, w;
    double unit_price() { return 1.0 * v / w; }
};

bool cmp(const Item &a, const Item &b) {
    return a.unit_price() > b.unit_price();  // 按性价比降序
}

double knapsack(vector<Item> &items, int W) {
    sort(items.begin(), items.end(), cmp);
    double total_v = 0;
    for (auto &item : items) {
        if (W >= item.w) {
            total_v += item.v;
            W -= item.w;
        } else {
            total_v += item.unit_price() * W;
            break;
        }
    }
    return total_v;
}

注意:整数背包(0/1背包)不能用贪心,必须用DP!CSP-J常考0/1背包,看清题目!


策略3:霍夫曼思想(合并代价最小)

问题:N堆果子,每次合并两堆,代价为两堆重量和,求最小总代价。

证明:每次合并最小的两堆,总代价最小。

// 用优先队列实现
priority_queue<long long, vector<long long>, greater<long long>> pq;
long long ans = 0;
while (pq.size() > 1) {
    long long a = pq.top(); pq.pop();
    long long b = pq.top(); pq.pop();
    ans += a + b;
    pq.push(a + b);
}

策略4:字典序贪心(字符串构造)

问题:构造字典序最小的字符串、删除k个字符使结果最小。

模板

// 删除k个字符使结果最小
string removeKdigits(string s, int k) {
    string res;
    for (char c : s) {
        // 如果当前字符比栈顶小,且还能删,就弹出栈顶
        while (k > 0 && !res.empty() && res.back() > c) {
            res.pop_back();
            k--;
        }
        res += c;
    }
    // 如果k还有剩余,从末尾删除
    res.resize(res.size() - k);
    // 去除前导零
    return res.empty() ? "0" : res;
}

核心:维护单调栈,保持递增序列。


策略5:后悔贪心(资源受限)

问题:选最多任务,但总时间/成本有限制(如公路题)。

模板

// 按收益排序,超预算时踢掉已选中最差的
priority_queue<int> pq;  // 存已选代价(大根堆)
int sum = 0, ans = 0;
for (auto &task : tasks) {
    pq.push(task.cost);
    sum += task.cost;
    if (sum > budget) {
        sum -= pq.top();  // 踢掉最大的
        pq.pop();
    }
    ans = max(ans, 当前收益);
}

三、CSP-J高频题型与识别技巧

题型1:区间覆盖问题

特征:用最少的点/区间覆盖所有区间 关键词:"至少选几个"、"覆盖所有"

识别:将区间按右端点排序,贪心选择


题型2:排队等待问题

特征:最小化总等待时间 关键词:"最小化等待时间总和"

技巧:按时间从小到大排序,依次处理


题型3:资源分配问题

特征:有限资源,优先分配给"性价比"高的 关键词:"最多完成几个"、"最大收益"

技巧:按性价比排序,从大到小选


题型4:字符串删除问题

特征:删除k个字符使结果最优 关键词:"删除k个"、"字典序最小"

技巧:单调栈维护,删除"山峰"(前>后)


四、贪心证明方法(CSP-J不用严格证,但要会口胡)

  1. 反证法:假设最优解不包含贪心选择,替换后更优,矛盾

  2. 交换论证:将最优解中的某个选择换成贪心选择,结果不变差

  3. 边界法:最优解的第一个选择一定是贪心选择

竞赛口诀先排序,后贪心,能选就选不能就踢,最后更新答案。


五、常见陷阱与调试技巧

陷阱1:0/1背包用贪心(必WA)

// ❌ 错误:整数背包用性价比贪心
if (w[i] <= W) { take; W -= w[i]; }  // WA!

// ✅ 正确:用DP
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);

区分分数背包可贪心,整数背包必须用DP!


陷阱2:忘记排序

// 区间调度前必须排序
sort(acts.begin(), acts.end(), [](a,b){ return a.end < b.end; });

陷阱3:long long溢出

// 累加和必须long long
long long sum = 0;  // ✓
int sum = 0;        // ✗ 溢出见祖宗

六、CSP-J贪心万能模板

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

struct Node {
    int s, e, v;  // 开始、结束、价值
};

bool cmp(const Node &a, const Node &b) {
    return a.e < b.e;  // 按结束时间排序(根据实际情况改)
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n;
    cin >> n;
    vector<Node> a(n);
    for (auto &x : a) cin >> x.s >> x.e >> x.v;
    
    sort(a.begin(), a.end(), cmp);  // 1. 排序
    
    // 2. 维护贪心状态(优先队列、计数器等)
    priority_queue<int> pq;
    ll sum = 0, ans = 0;
    
    // 3. 遍历贪心
    for (auto &x : a) {
        // 4. 检查限制,必要时踢掉
        if (违反限制) {
            // 踢掉最差选择
            sum -= pq.top();
            pq.pop();
        }
        // 5. 选当前
        pq.push(x.v);
        sum += x.v;
        ans = max(ans, sum);
    }
    
    cout << ans << endl;
    return 0;
}

七、核心记忆口诀

贪心三步走

  1. 排排序:按某个关键字排序

  2. 看看看:遍历检查是否违反限制

  3. 踢踢踢:违反就踢掉最差的选择

四句保命真言

先想排序,再想贪心,能选则选,不能则滚


八、一句话总结

CSP-J贪心题 = 排序 + 局部最优 + 后悔机制,看到"最多/最少/最大/最小",先排序,再维护一个优先队列踢掉差的,答案就出来了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值