CSP 2018-09-4(再买菜)30行代码满分暴力搜索+剪枝

题目描述

在这里插入图片描述

思路

题目有点隐晦,读懂后感觉是暴力搜索题目,就做了,剪枝后可以满分,程序也很短。看了一圈CSDN,大多数同学是用的差分约束,SPFA找负边权图的最长路。。。我人傻了,做的时候就是在想18年9月这套题怎么没有图论。。。不过我这个水平完全不会朝图论去想。
我们可以假设第一个商店第一天的价格是1,那么可以依次推出后面商店的价格。由于是去尾法取整,第二个商店可能有两种价格(余数为0或1),后面的商店由于是三天价格的平均(余数为0或1或2),都有三种价格,算到最后一个商店再来验证,若最后两个商店的价格平均是最后一个商店第二天价格,那么就是一个解,否则退回到第一个商店,价格加1。
我们每次搜索的时候从余数为0开始,找到解就exit程序,这样得到的结果就是字典序最小的。

剪枝

虽然第一天价格没有数据范围提示,但是要求输出正整数,隐含了价格至少为1的信息,递归过程中出现价格小于1的商店直接return。这个剪枝可以从40分到80分
80分到100分有点难考虑,我们得到第一个解是字典序最小的解,会直接输出并退出。所以return回来的都是错误的,下次考虑的时候,要去想这一个商店的价格是不是已经考虑过了,已经考虑过了而没有输出、退出那就说明是不可行的,无需再考虑。但是只考虑一个商店价格不行,会降到20分,因为当前考虑的商店价格范围由它的前两个商店确定,它们当中有可能前一个价格不同但是后一个考虑了同一个价格,所以要把前一个的价格也考虑上来剪枝。这样就可以完全确定后面所有商店的价格范围。
如果不好理解,可以看下面有详细注释的代码

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 310;//商店数量最大值 
int n,re[N],tool[N],jud[N][N][N];//n:商店个数 re[N]:第二天价格 tool[N]:第一天价格 jud[x][y][z]:第x个商店价格y且前一个价格为z是否已考虑 
void dfs(int val, int now, int n){  //赋给now商店价格val,一共n个商店 
	if(val<1||jud[now][val][tool[now-1]]) return; //剪枝,价格不为正整数或已经访问这种情况且未得到答案 
	else{
		tool[now] = val; //赋值 
		jud[now][val][tool[now-1]] = 1; //标记已访问 
	}
	if(now==1){ //当前考虑第一家商店 
		dfs((re[now]<<1)-val, now+1, n);
		dfs((re[now]<<1)-val+1, now+1, n); //第二家商店价格有两种情况 
	}
	else if(now!=n){
		dfs(3*re[now]-val-tool[now-1], now+1, n);
		dfs(3*re[now]-val-tool[now-1]+1, now+1, n);
		dfs(3*re[now]-val-tool[now-1]+2, now+1, n);//后面所有商店价格都是3种情况 
	}
	else{
		if(re[now]==(tool[now]+tool[now-1])>>1){ //验证最后一个商店价格是否正确 
			for(int i=1; i<=n; i++) printf("%d ",tool[i]);//正确就输出并退出程序 
			exit(0);
		}
	}
}
int main(){
	scanf("%d",&n);//输入商店数 
	for(int i=1; i<=n; i++) scanf("%d",&re[i]);//输入第二天价格 
	for(int i=1;i<=2*re[1];i++) dfs(i,1,n);//第一家商店最多有2*re[1]种价格情况 
	return 0;
} 

提交结果

在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值