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.csdn.net/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;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值