题目就不贴了。
题目要求:给出商店的第二天的菜价,第二天的菜价是第一天临近商店的价格的平均值(求平均时使用去尾法求整),要求找到符合要求的第一天菜价中字典序最小的一种。下面分别使用
a
,
b
a,b
a,b表示第一天,第二天的菜价,由题意可得,菜价应满足下列不等式:(因为价格是整数,所以将等式右边的值减一,则可将原来的
<
<
< 换为
≤
\le
≤)
2
b
1
≤
a
1
+
a
2
<
2
(
b
1
+
1
)
→
2
b
1
≤
a
1
+
a
2
≤
2
b
1
+
1
2b_1 \le a_1+a_2 < 2(b_1+1) \ \rightarrow \ 2b_1 \le a_1+a_2 \le 2b_1+1 \
2b1≤a1+a2<2(b1+1) → 2b1≤a1+a2≤2b1+1
3
b
2
≤
a
1
+
a
2
+
a
3
≤
3
b
1
+
2
3b_2 \le a_1+a_2+a_3 \le 3b_1+2
3b2≤a1+a2+a3≤3b1+2
3
b
3
≤
a
2
+
a
3
+
a
4
≤
3
b
3
+
2
3b_3 \le a_2+a_3+a_4 \le 3b_3+2
3b3≤a2+a3+a4≤3b3+2
⋅
⋅
⋅
···
⋅⋅⋅
⋅
⋅
⋅
⋅
····
⋅⋅⋅⋅
3
b
n
−
1
≤
a
n
−
2
+
a
n
−
1
+
a
n
≤
3
b
n
−
1
+
2
3b_{n-1} \le a_{n-2}+a_{n-1}+a_{n} \le 3b_{n-1}+2
3bn−1≤an−2+an−1+an≤3bn−1+2
2
b
n
≤
a
n
−
1
+
a
n
≤
2
b
n
+
1
2b_{n} \le a_{n-1}+a_{n} \le 2b_{n}+1
2bn≤an−1+an≤2bn+1
思路一:纯dfs(80分) :现在约束已经有了,等式左右两边的值都是已知的,最直接的想法就是从小到大枚举
a
1
a_1
a1可能的取值(要求字典序最小所以对于任意
a
i
a_i
ai的取值都应从小到大枚举),
a
1
a_1
a1确定后再根据第一个不等式考虑
a
2
a_2
a2可能的取值,
a
2
a_2
a2确定后考虑
a
3
a_3
a3····,这样不断进行下去,最后如果
a
n
a_n
an的值也满足要求,则当前的序列就是所需的字典序最小的序列。
思路二:dfs+剪枝(100分):第一个方法的问题在于每次尝试都要计算到第n天或者遇到中间某天没有可能的取值时才能得出本次尝试的结果,当n比较大时就会因为可能的情况太多而超时。为了减少尝试的次数,可以使用记忆化剪枝的方法。
具体来说就是使用变量isfind表示是否已找到最小字典序序列,用visit[i][curr][pre] 数组来表示 第i天的菜价设为curr,第i-1天设为pre 这种情况是否已经尝试过,如果尝试过就直接返回(不再进行尝试),没有尝试过才执行后面的。下面说一下为什么这种方法有效,一开始可能会认为为了求字典最小的序列我们的尝试的取值都是从小到大的,怎么可能存在尝试过的再一次被尝试?这个问题的答案就是:我们尝试第i天的取值时的确是从小到大进行尝试,当第i天的取值为a时,对于第i+1,i+2两天,假设它们的取值为b,c,假设这种尝试不满足题目要求,函数会返回(此时isfind的值为false),接下来第i天的值被设为a+1,对于第i+1,i+2天,假设它们又分别被设置为了b,c,这次我们就能确定这种尝试是没有必要的,因为之前尝试过了,并没有找到可行的解(如果之前找到可行解程序已经退出),所以可以不用再进行尝试,通过这种方式就能较少尝试的次数,从而避免超时。
下面是两种思路的代码。
思路一代码(80分):
#include <bits/stdc++.h>
using namespace std;
int d1[305],d2[305];
int n;
// 第i天的价格设为value是否可能
bool isValid(int i,int value)
{
d1[i] = value;
if(i == n)
{
return d1[n-1] + d1[n] >= 2 * d2[n] && d1[n-1] + d1[n] < 2 * (d2[n] + 1);
}
else if(i == 1)
{
for(int v = 2*d2[1]-d1[1];v < 2*(d2[1]+1)-d1[1]; v++)
{
if(v >= 1 && isValid(2,v))
return true;
}
}
else
{
for(int v = 3*d2[i]-d1[i-1]-d1[i];v< 3*(d2[i]+1)-d1[i-1]-d1[i]; v++)
if(v >= 1 && isValid(i+1,v))
return true;
}
return false;
}
void solve()
{
for(int v = 1;v < 2*(d2[1]+1); v++)
{
if(isValid(1,v))
break;
}
}
int main(int argc, char const *argv[])
{
cin >> n;
for(int i = 1;i <= n; i++)
scanf("%d",&d2[i]);
solve();
for(int i = 1;i <= n; i++)
printf("%d ",d1[i]);
cout << endl;
return 0;
}
思路二代码(100分):
#include <bits/stdc++.h>
using namespace std;
const int maxn = 400;
int n;
int day1[maxn],day2[maxn];
bool visit[maxn][maxn][maxn];
bool isfind = false;
void dfs(int i,int curr,int pre)
{
if(visit[i][curr][pre])
return ;
visit[i][curr][pre] = 1;
day1[i] = curr;
if(i == n)
{
if((curr + pre) / 2 == day2[n])
{
isfind = true;
return ;
}
}
else
{
int next = day2[i]*3 - curr - pre;
for(int k = 0;k < 3; k++)
{
if(next+k >= 1 && !isfind)
dfs(i+1,next+k,curr);
}
}
}
int main(int argc, char const *argv[])
{
cin >> n;
for(int i = 1;i <= n; i++)
cin >> day2[i];
memset(visit,0,sizeof(visit));
for(int v1 = 1;v1 < 2*day2[1]; v1++)
{
day1[1] = v1;
dfs(2,2*day2[1]-v1,day1[1]);
if(!isfind)
dfs(2,2*day2[1]-v1+1,day1[1]);
else
break;
}
for(int i = 1;i <= n; i++)
cout << day1[i] << " ";
cout << endl;
return 0;
}
本文探讨了一个关于预测第二天菜价的问题,通过优化第一天菜价以满足字典序最小的要求。介绍了两种解决策略:纯dfs算法实现(80分)及dfs+剪枝优化算法(100分)。详细解析了剪枝优化如何减少不必要的尝试,避免超时,提供了具体的代码实现。
460

被折叠的 条评论
为什么被折叠?



