1489 蜥蜴和地下室 51NOD

16 篇文章 0 订阅
12 篇文章 1 订阅

哈利喜欢玩角色扮演的电脑游戏《蜥蜴和地下室》。此时,他正在扮演一个魔术师。在最后一关,他必须和一排的弓箭手战斗。他唯一能消灭他们的办法是一个火球咒语。如果哈利用他的火球咒语攻击第i个弓箭手(他们从左到右标记),这个弓箭手会失去a点生命值。同时,这个咒语使与第i个弓箭手左右相邻的弓箭手(如果存在)分别失去b(1 ≤ b < a ≤ 10)点生命值。

因为两个端点的弓箭手(即标记为1和n的弓箭手)与你相隔较远,所以火球不能直接攻击他们。但是哈利能用他的火球攻击其他任何弓箭手。

每个弓箭手的生命值都已知。当一个弓箭手的生命值小于0时,这个弓箭手会死亡。请求出哈利杀死所有的敌人所需使用的最少的火球数。

如果弓箭手已经死亡,哈利仍旧可以将他的火球扔向这个弓箭手。

Input

第一行包含3个整数 n, a, b (3 ≤ n ≤ 10; 1 ≤ b < a ≤ 10),第二行包含n个整数——h1,h2,…,hn (1 ≤ hi ≤ 15), hi 是第i个弓箭手所拥有的生命力。

Output

以一行输出t——所需要的最少的火球数。

Input示例

3 2 1
2 2 2

Output示例

3

想了好久的一个DP题(好弱,,,),两端敌人是无法直接攻击到的,杀死他们的办法是唯一的,所以首先贪心处理两端的敌人,然后题目要求有点奇葩,每个人还得生命值小于0才死,所以在开始直接给每个人的生命值+1,然后当成生命值为0的时候死去做。
之后对处理完的敌人可以这样考虑,首先定义状态:
dp[i][j][k][l]表示对于第i个人,自己状态为k,前一个人状态为j,后一个人状态为l时所需发射最小火球术。
如果你对第i个人进行直接火球攻击,那么对i,j,k分别减去对应的值,得到子状态进行更新,然后如果j==0时,还应更新:
dp[i+1][k][l][num[i+2]]的值。

#include<bits/stdc++.h>
using namespace std;
#define D(x,t) x-a*t>0?x-t*a:0
#define ID(x,t) x-b*t>0?x-t*b:0
const int INF=0X7fffffff;
int dp[20][20][20][20];  //dp[i][i-1][i][i+1]:当对第i个敌人直接发射火球时可能的状态,i为第k-1的人,j为第k个人
int n,a,b;

int main()
{
   while(cin>>n>>a>>b)
   {
       int num[25];
       int sum_times=0;
       memset(num,0,sizeof(num));
       for(int i=0;i<n;i++)
       {
           scanf("%d",&num[i]);
           num[i]++;
       }

       //先对数据进行处理
       int times=ceil((double)num[0]/b);
       sum_times+=times;
       num[1]=D(num[1],times);
       num[2]=ID(num[2],times);
       times=ceil((double)num[n-1]/b);
       sum_times+=times;
       num[n-2]=D(num[n-2],times);
       num[n-3]=ID(num[n-3],times);
       num[0]=num[n-1]=0;

       //初始化为无穷大
       for(int i=0;i<20;i++)
        for(int j=0;j<20;j++)
         for(int k=0;k<20;k++)
           for(int l=0;l<20;l++)
          dp[i][j][k][l]=INF;

       dp[1][0][num[1]][num[2]]=0;
       for(int i=1;i<n-1;i++)
        for(int l=num[i+1];l>=0;l--)
         for(int k=num[i];k>=0;k--)
          for(int j=num[i-1];j>=0;j--)
          {
            if(dp[i][j][k][l]!=INF)
            {
                int z=0;
              while(1)
              {
              //    cout<<2<<endl;
                int aa=ID(j,z);
                int bb=D(k,z);
                int cc=ID(l,z);

                dp[i][aa][bb][cc]=min(dp[i][j][k][l]+z,dp[i][aa][bb][cc]);
                if(!aa)
                 dp[i+1][bb][cc][num[i+2]]=min(dp[i][j][k][l]+z,dp[i+1][bb][cc][num[i+2]]);
                z++;
                if(!bb&&!aa&&!cc) break;
              }
            }
          }
          cout<<dp[n-1][0][0][0]+sum_times<<endl;
   }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值