CodeForces 6 D.Lizards and Basements 2(dp)

202 篇文章 1 订阅

Description

n n 个怪物编号1~ n n ,每次可以攻击2~ n1 n − 1 中任意一个怪物,第 i i 个怪物的血量为hi,一个怪物必须血量为负值才算死亡,但是死亡的怪物依旧可以攻击,如果对第 i i 个怪物攻击,对第i的怪物会造成 a a 点伤害,对第i1和第 i+1 i + 1 个怪物造成 b b 点伤害,问最少多少次攻击可以让所有怪物死亡,并输出被攻击怪物的编号

Input

第一行三个整数n,a,b,之后输入 n n 个整数hi (3n10,1b<a10,1hi15) ( 3 ≤ n ≤ 10 , 1 ≤ b < a ≤ 10 , 1 ≤ h i ≤ 15 )

Output

输出最少攻击次数和每次攻击的怪物编号

Sample Input

3 2 1
2 2 2

Sample Output

3
2 2 2

Solution

攻击先后次序无关,只需考虑对每只怪物攻击几次即可,故顺序考虑攻击,即如果当前对第 i i 个怪物攻击,那么要求前i2个怪物都已经死亡,为避免计算繁琐,把每个怪物的血量加一,这样只要怪物血量清零则死亡,以 dp[i][j][k] d p [ i ] [ j ] [ k ] 表示当前攻击第 i i 个怪物,前i2个怪物已经死亡,第 i1 i − 1 个怪物血量为 j j ,第i个怪物的血量为 k k 的条件下需要的最少攻击次数,则有dp[2][h1][h2]=0,考虑对第 i i 个怪物的攻击次数,首先必须让第i1个怪物死亡才能去攻击第 i+1 i + 1 个怪物,所以至少要攻击 x=jb x = ⌈ j b ⌉ 次,最多次数是使得第 i i 个怪物和第i+1个怪物都死亡,打死第 i i 个怪物需要攻击y=ka次,打死第 i+1 i + 1 个怪物需要攻击 z=hi+1b z = ⌈ h i + 1 b ⌉ 次,那么最多攻击次数即为 max(y,z) m a x ( y , z ) ,枚举攻击次数 l l ,则第i个怪物血量变成 jj=max(0,kal) j j = m a x ( 0 , k − a ∗ l ) ,第 i+1 i + 1 个怪物血量变成 kk=max(0,hi+1bl) k k = m a x ( 0 , h i + 1 − b ∗ l ) ,进而有转移:

dp[i+1][jj][kk]=max(dp[i+1][jj][kk],dp[i][j][k]+l) d p [ i + 1 ] [ j j ] [ k k ] = m a x ( d p [ i + 1 ] [ j j ] [ k k ] , d p [ i ] [ j ] [ k ] + l )

dp[n][0][0] d p [ n ] [ 0 ] [ 0 ] 即为答案,对每个怪物的具体攻击次数在转移时记录转移到当前状态的前继状态即可

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef pair<int,int>P;
int n,a,b,h[11],dp[11][22][22];
P pre[11][22][22];
#define INF 200
void output(int id,int x,int y)
{
    if(id==1)return ;
    int xx=pre[id+1][x][y].first,yy=pre[id+1][x][y].second;
    output(id-1,xx,yy);
    for(int i=0;i<dp[id+1][x][y]-dp[id][xx][yy];i++)printf("%d ",id);
}
int main()
{
    scanf("%d%d%d",&n,&a,&b);
    for(int i=1;i<=n;i++)scanf("%d",&h[i]),h[i]++;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=16;j++)
            for(int k=0;k<=16;k++)
                dp[i][j][k]=INF;
    dp[2][h[1]][h[2]]=0;
    for(int i=2;i<n;i++)
        for(int j=0;j<=h[i-1];j++)
            for(int k=0;k<=h[i];k++)
                if(dp[i][j][k]!=INF)
                {
                    int mn=(j+b-1)/b;
                    int mx=max(mn,max((k+a-1)/a,(h[i+1]+b-1)/b));
                    for(int l=mn;l<=mx;l++)
                    {
                        int jj=max(0,k-l*a),kk=max(0,h[i+1]-l*b);
                        if(dp[i+1][jj][kk]>dp[i][j][k]+l)
                        {
                            dp[i+1][jj][kk]=dp[i][j][k]+l;
                            pre[i+1][jj][kk]=P(j,k);
                        }
                    }
                }
    printf("%d\n",dp[n][0][0]);
    output(n-1,0,0);
    printf("\n");
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值