Lizards and Basements 2
题面翻译
题意
有一队人,你可以用火球点某个人,会对当前人造成a点伤害,对旁边的人造成b点伤害。
不能打1号和n号,求最少多少发点死所有人。
题目描述
This is simplified version of the problem used on the original contest. The original problem seems to have too difiicult solution. The constraints for input data have been reduced.
Polycarp likes to play computer role-playing game «Lizards and Basements». At the moment he is playing it as a magician. At one of the last levels he has to fight the line of archers. The only spell with which he can damage them is a fire ball. If Polycarp hits the $ i $ -th archer with his fire ball (they are numbered from left to right), the archer loses $ a $ health points. At the same time the spell damages the archers adjacent to the $ i $ -th (if any) — they lose $ b $ ( $ 1<=b<a<=10 $ ) health points each.
As the extreme archers (i.e. archers numbered 1 and $ n $ ) are very far, the fire ball cannot reach them. Polycarp can hit any other archer with his fire ball.
The amount of health points for each archer is known. An archer will be killed when this amount is less than 0. What is the minimum amount of spells Polycarp can use to kill all the enemies?
Polycarp can throw his fire ball into an archer if the latter is already killed.
输入格式
The first line of the input contains three integers $ n,a,b $ ( $ 3<=n<=10 $ ; $ 1<=b<a<=10 $ ). The second line contains a sequence of $ n $ integers — $ h_{1},h_{2},…,h_{n} $ ( $ 1<=h_{i}<=15 $ ), where $ h_{i} $ is the amount of health points the $ i $ -th archer has.
输出格式
In the first line print $ t $ — the required minimum amount of fire balls.
In the second line print $ t $ numbers — indexes of the archers that Polycarp should hit to kill all the archers in $ t $ shots. All these numbers should be between 2 and $ n-1 $ . Separate numbers with spaces. If there are several solutions, output any of them. Print numbers in any order.
样例 #1
样例输入 #1
3 2 1
2 2 2
样例输出 #1
3
2 2 2
样例 #2
样例输入 #2
4 3 1
1 4 1 1
样例输出 #2
4
2 2 3 3
思路
这道题如果单纯暴力搜索的话(我们此时是按答案的长度为迭代的次数(也就是暴力枚举答案的某个位数填的是什么数字)),会卡在# 43。(见我代码注释掉的代码)
为什么会卡住呢?因为当w[i]比较大,但a,b比较小的时候,那么这个时候我们的搜索次数就会大大增加,因此就会超时。
因此,对于一次一次减超时的情况,我们可以采取一次减非常多(我们直接按顺序减,为什么呢?因为每个w[i]减去的次数是有限的,而且w[i]<15,所以次数不怎么多),此时我们只需在dfs的参数上新增一个k,用来记录上一次减的是哪个数字。也就是如图:
但我们还得注意的是:我刚开始写的时候是每遍历完都要判断是否血量小于0,这个太耗时了,因此我们采取:
具体参考
代码(卡#21)
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N = 20;
int w[N];
bool st[N];
int pre[N],c[N];
int n, a, b;
int ans = 2e9;
void dfs(int u) {
if (ans <= u) {
return;
}
bool f = false;
for (int i = 1; i <= n; i++) {
if (w[i] >= 0) {
f = true;
break;
}
}
if (!f) {
if (ans >= u) {
ans = u;
for (int i = 1; i <= ans; i++) {
pre[i] = c[i];
}
}
return;
}
for (int i = 2; i < n; i++) {
if (w[i] < 0 && w[i - 1] < 0 && w[i + 1] < 0)continue;
w[i] -= a, w[i - 1] -= b, w[i + 1] -= b;
c[u+1] = i;
dfs(u + 1);
w[i] += a, w[i - 1] += b, w[i + 1] += b;
}
}
int main() {
cin >> n >> a >> b;
for (int i = 1; i <= n; i++) {
cin >> w[i];
}
dfs(1);
cout << ans-1 << endl;
for (int i = 2; i <= ans; i++) {
cout << pre[i] << " ";
}
return 0;
}
代码(卡#43)
//我们可以让dfs的参数为答案的第几位
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 110;
int w[N];
int n,a,b;
int c[N],pre[N];
int ans=2e9;
void dfs(int u,int start){
if(u>=ans){
return;
}
bool f=false;
for(int i=1;i<=n;i++){
if(w[i]>=0){
f=true;
break;
}
}
if(!f){
ans=min(ans,u);
for(int i=0;i<ans;i++){
pre[i]=c[i];
}
}
for(int i=start;i<n;i++){
if(w[i]<0&&w[i-1]<0&&w[i+1]<0)continue;
w[i]-=a;
w[i-1]-=b,w[i+1]-=b;
c[u]=i;
dfs(u+1,i);
w[i]+=a;
w[i-1]+=b,w[i+1]+=b;
}
}
int main(){
cin>>n>>a>>b;
for(int i=1;i<=n;i++){
cin>>w[i];
}
dfs(0,2);
cout<<ans<<endl;
for(int i=0;i<ans;i++){
cout<<pre[i]<<' ';
}
return 0;
}
代码(100分)
//我们可以让dfs的参数为答案的第几位
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 110;
int w[N];
int n,a,b;
int c[N],pre[N];
int ans=2e9;
void dfs(int u,int start,int k){
if(u>=ans){
return;
}
// bool f=false;
// for(int i=1;i<=n;i++){
// if(w[i]>=0){
// f=true;
// break;
// }
// }
// if(!f){
// ans=min(ans,u);
// for(int i=0;i<ans;i++){
// pre[i]=c[i];
// }
// }
if(k>=n){
if(w[k]>=0||w[n]>=0)return;
ans=min(ans,u);
for(int i=0;i<ans;i++){
pre[i]=c[i];
}
return;
}
//单纯这样写会超时,为什么呢?因为你当a很小但你的值很大,减的次数就很多,因此我们就模拟减的过程即可
// for(int i=start;i<n;i++){
// if(w[i]<0&&w[i-1]<0&&w[i+1]<0)continue;
// w[i]-=a;
// w[i-1]-=b,w[i+1]-=b;
// c[u]=i;
// dfs(u+1,i);
// w[i]+=a;
// w[i-1]+=b,w[i+1]+=b;
// }
for(int i=0;i<=max(w[k]/a+1,max(w[k-1]/b+1,w[k+1]/b+1));i++){
if(w[k-1]-i*b>=0)continue;
w[k]-=i*a;
w[k-1]-=i*b,w[k+1]-=i*b;
for(int j=u;j<=u+i;j++)c[j]=k;
dfs(u+i,i,k+1);
w[k]+=i*a;
w[k-1]+=i*b,w[k+1]+=i*b;
}
}
int main(){
cin>>n>>a>>b;
for(int i=1;i<=n;i++){
cin>>w[i];
}
dfs(0,2,2);
cout<<ans<<endl;
for(int i=0;i<ans;i++){
cout<<pre[i]<<' ';
}
return 0;
}