CCF 201809-4 再卖菜 AC代码

// 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;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值