201809-4 再卖菜 ccf

问题描述
  在一条街上有n个卖菜的商店,按1至n的顺序排成一排,这些商店都卖一种蔬菜。
  第一天,每个商店都自己定了一个正整数的价格。店主们希望自己的菜价和其他商店的一致,第二天,每一家商店都会根据他自己和相邻商店的价格调整自己的价格。具体的,每家商店都会将第二天的菜价设置为自己和相邻商店第一天菜价的平均值(用去尾法取整)。
  注意,编号为1的商店只有一个相邻的商店2,编号为n的商店只有一个相邻的商店n-1,其他编号为i的商店有两个相邻的商店i-1和i+1。
  给定第二天各个商店的菜价,可能存在不同的符合要求的第一天的菜价,请找到符合要求的第一天菜价中字典序最小的一种。
  字典序大小的定义:对于两个不同的价格序列(a1, a2, …, an)和(b1, b2, b3, …, bn),若存在i (i>=1), 使得ai<bi,且对于所有j<i,aj=bj,则认为第一个序列的字典序小于第二个序列。
输入格式
  输入的第一行包含一个整数n,表示商店的数量。
  第二行包含n个正整数,依次表示每个商店第二天的菜价。
输出格式
  输出一行,包含n个正整数,依次表示每个商店第一天的菜价。
样例输入
8
2 2 1 3 4 9 10 13
样例输出
2 2 2 1 6 5 16 10
数据规模和约定
  对于30%的评测用例,2<=n<=5,第二天每个商店的菜价为不超过10的正整数;
  对于60%的评测用例,2<=n<=20,第二天每个商店的菜价为不超过100的正整数;
  对于所有评测用例,2<=n<=300,第二天每个商店的菜价为不超过100的正整数。
  请注意,以上都是给的第二天菜价的范围,第一天菜价可能会超过此范围。

整体上参考了https://blog.youkuaiyun.com/imotolove/article/details/82777819的思路,采用dfs搜索并优化剪枝,这里解释一下为什么利用isvis[400][400][400]进行优化是行之有效的,因为对于第i天来说,如果前一天pre和今天cur确定了,那么第三天也确定了(可能在此基础上加0,1,2),对后续的搜索结果也确定了,如果当前策略行不通(已经搜索过),那么自然就没必要往后面继续搜索了

其他一些博客采用了差分约束的解法,

dfs的解法及注释如下

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

int day2[400], day1[400], n;
bool isfind = 0, isvis[400][400][400];
void dfs(int i, int cur, int pre){
	if(isvis[i][cur][pre]) return;//当前状态已经搜索过,进行剪枝
	isvis[i][cur][pre] = 1;
	day1[i] = cur;
	if(i == n){
		if((day1[n - 1] + day1[n]) / 2 == day2[n]) isfind = 1;//核对最后一天
		return;
	}
	int next = 3*day2[i] - pre - cur;//对第i天,如果该天cur,以及昨天pre确定,那么下一天有3种情况
	for(int k = 0; k < 3; ++k){//0, 1, 2
		if(next + k > 0 && !isfind){//注意next + k必须为正整数
			dfs(i + 1, next + k, cur);
		}
	}
}
int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i){
		scanf("%d", day2 + i);
	}
	for(int e = 1; e <= 2*day2[1]; ++e){
		day1[1] = e;//第一天确定,那么第二天也能确定,有两种情况
		dfs(2, 2*day2[1] - e, day1[1]);
		if(!isfind) dfs(2, 2*day2[1] - e + 1, e);
		else break;
	}
	for(int i = 1; i <= n; ++i){
		printf("%d ", day1[i]);
	}
	return 0;
}
### CCF CSP 2018年9月 Python真题及答案解析 #### 题目概述 CCF CSP 2018年9月的第一题涉及卖菜问题,第二题则围绕买菜展开。这两道题目均考察了基本的数据处理能力和逻辑思维能力。对于初学者来说,理解输入输出的要求以及边界条件尤为重要[^2]。 #### 卖菜 (201809-1) ##### 描述 给定一个长度为 n 的整数数组 a 和两个参数 p 和 q,计算满足以下条件的子区间数量: - 子区间的最小值大于等于 p; - 子区间的最大值小于等于 q。 ##### 解析 该问题可以通过滑动窗口算法高效解决。具体而言,维护当前窗口内的最小值和最大值,并动态调整窗口大小以统计符合条件的子区间数目。以下是基于此思路的一个实现: ```python def count_subarrays(n, p, q, array): from collections import deque min_deque = deque() max_deque = deque() result = 0 left = 0 for right in range(n): while min_deque and array[right] < array[min_deque[-1]]: min_deque.pop() min_deque.append(right) while max_deque and array[right] > array[max_deque[-1]]: max_deque.pop() max_deque.append(right) while array[min_deque[0]] < p or array[max_deque[0]] > q: if min_deque[0] == left: min_deque.popleft() if max_deque[0] == left: max_deque.popleft() left += 1 result += right - left + 1 return result # 输入部分 n, p, q = map(int, input().split()) array = list(map(int, input().split())) print(count_subarrays(n, p, q, array)) ``` 上述代码实现了双端队列来追踪当前窗口的最大值与最小值,从而快速判断是否满足约束条件并更新计数值。 --- #### 买菜 (201809-2) ##### 描述 某人每天可以购买一定量的商品,每种商品的价格不同。已知未来几天各商品价格变化情况,请设计一种策略使得总花费最少。 ##### 解析 这是一个典型的贪心算法应用案例。核心思想在于优先选择单位成本最低的商品进行采购,直到达到每日需求上限为止。下面展示了一种可能的解决方案: ```python from heapq import heappop, heappush def minimal_cost(days, daily_needs, prices_per_day): total_cost = 0 available_items = [] for day_index in range(len(daily_needs)): current_price = prices_per_day[day_index] # 将当天所有可选物品加入候选列表 for item_id, price in enumerate(current_price): heappush(available_items, (price, item_id)) need_count = daily_needs[day_index] # 贪婪选取最便宜的选项直至满足当日需求 while need_count > 0 and available_items: cheapest_item = heappop(available_items)[0] total_cost += cheapest_item need_count -= 1 return total_cost # 示例数据读取方式 days = int(input()) daily_needs = list(map(int, input().strip().split())) prices_per_day = [] for _ in range(days): row_prices = tuple(map(float, input().strip().split())) prices_per_day.append(row_prices) result = minimal_cost(days, daily_needs, prices_per_day) print(result) ``` 这里利用堆结构保持全局最优的选择顺序,在每次循环中不断补充新的备选项至堆内以便后续操作。 --- ### 总结 以上两道题目分别代表了不同的编程技巧——前者侧重于窗口管理技术的应用;后者体现了如何通过适当的数据结构优化贪婪决策过程。两者共同强调了对细节的关注以及对标准输入/输出格式严格遵循的重要性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值