// 201809-4 再卖菜
// 深度优先搜索dfs + 可行性剪枝 +记忆化搜索
/*
* 可行性剪枝:因为(d1[i-2]+d1[i-1]+d1[i])/3=d2[i-1]
* 因此在推导第i家店的价格时,不用从1-300遍历,可直接根据等式缩小判断范围
*/
/*
*记忆化搜索:因为第i家店状态是根据前两家店扩展,所以若从前两家的状态无法扩展到最后
*则表示该状态是无法得到结果的,因此以后搜索再遇到该状态时可以直接返回,不用继续搜索
*
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn = 305;
int n, d1[maxn], d2[maxn]; // d1表示第一天的价格 d2表示第二天的价格
int vis[maxn][maxn][maxn]; // vis[x][i][j]表示当前点是x,前一家店的价格是i,前两家店的价格时j
void dfs(int k) // 确定第k个店铺的价格
{
if (k > n) // 所有店铺的价格均确定
{
// 检查最后一家店是否满足条件(由前一家店搜索过来,因此前一家店的约束一定满足)
if ((d1[n - 1] + d1[n]) / 2 == d2[n])
{
// 因为是按照字典序升序进行搜索的,则该答案一定是字典序最小的
for (int i = 1; i <= n; ++i)
{
cout << d1[i] << " ";
}
exit(0);
}
}
// 可行性剪枝(根据前两家店第一天的价格和前一家店第二天的价格推导)
for (int i = 3 * d2[k - 1] - d1[k - 1] - d1[k - 2]; i <= 3 * d2[k - 1] - d1[k - 1] - d1[k - 2] + 2; ++i) // 第k天可能价格
{
// 注:如果状态被访问过,则说明该状态时无法得到最终结果的,此时直接回溯,不再继续向后搜索
if (i >= 1 && i <= 300 && vis[k][d1[k - 1]][d1[k - 2]] == 0) // 价格满足合理范围 且 当前状态没有被扩展过
{
d1[k] = i;
dfs(k + 1); // 继续搜索第k天的菜价
}
}
// 状态(k,d1[k-1],d1[k-2])被访问过,此状态一定无法得到最终的结果,进行标记
vis[k][d1[k - 1]][d1[k - 2]] = 1; // 标记当前状态无法得到结果
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> d2[i];
for (int i = 1; i <= 300; ++i) // 第一家店的可能价格
{
d1[1] = i;
for (int j = 2 * d2[1] - d1[1]; j <= 2 * d2[1] - d1[1] + 1; ++j) // 第二家店的可能价格
{
if (j >= 1 && j <= 300) // 第一天菜价在合法范围内
{
d1[2] = j;
// 确定1、2家店的价格后,继续判断剩下店的价格
dfs(3);
}
}
}
return 0;
}
CCF 201809-4 再卖菜 AC代码
最新推荐文章于 2024-08-14 11:57:07 发布