题目描述
思路
题目有点隐晦,读懂后感觉是暴力搜索题目,就做了,剪枝后可以满分,程序也很短。看了一圈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;
}