CodeForces - 6D Lizards and Basements 2 (dfs + 回溯)

Lizards and Basements 2

题目大意:

有一队人,你可以用火球点某个人,会对当前人造成\(a\)点伤害,对旁边的人造成\(b\)点伤害。

不能打\(1\)号和\(n\)号,求最少多少发点死所有人。

输出最少次数和每次选择攻击的人的序列。

思路:

看数据范围容易想到暴力\(dfs\),由于\(1\)号点不能打,我们考虑从\(2\)号点开始\(dfs\),直到\(n-1\)号点。

\(dfs(x, sum)\)为前\(x\)个敌人至少需要攻击\(sum\)次。

注意判断是否能打掉最左边和最右边的敌人。

Code:
#include <bits/stdc++.h>
using namespace std;
const int N = 15;
const int INF = 0x3f3f3f3f;

int h[N], n, a, b, ans = INF;
vector<int> tmp, seq;

void dfs(int x, int sum) { //dfs(x, sum)表示打掉前x个弓箭手最少需要打sum发
    if (sum >= ans) return; //最优性剪枝
    if (x == n) { //搜索结束
        if (h[n] < 0) { //如果能把最右边的打掉则可更新答案
            ans = sum;
            seq = tmp;
        }
        return;
    }
    for (int i = 0; i <= max({h[x - 1] / b, h[x] / a, h[x + 1] / b}) + 1; i++) { //枚举攻击次数
        if (h[x - 1] - b * i < 0) { //如果能打掉最左边
            h[x - 1] -= b * i;
            h[x] -= a * i;
            h[x + 1] -= b * i;
            for (int j = 0; j < i; j++) { //按攻击次数加入到被攻击敌人序列中
                tmp.push_back(x);
            }
            dfs(x + 1, sum + i); //继续往后搜
            h[x - 1] += b * i; //回溯
            h[x] += a * i;
            h[x + 1] += b * i;
            for (int j = 0; j < i; j++) { //从序列中弹出
                tmp.pop_back();
            }
        }
    }
}

int main() {
    cin >> n >> a >> b;
    for (int i = 1;i <= n; i++) cin >> h[i];
    dfs(2, 0);
    cout << ans << endl;
    for (auto i : seq) cout << i << " ";
    cout << endl;
    return 0;
}
posted @ 2020-09-18 17:47  Nepenthe8  阅读(122)  评论(0编辑  收藏  举报