• [BZOJ 2964] Boss单挑战


    Link:https://www.lydsy.com/JudgeOnline/problem.php?id=2964

    Algorithm:

    一道很新颖的背包问题

    此题每个状态要维护的量巨多,而转移方式也巨多,直接写转移方程明显是不现实的

    那要考虑的就是能否将这些量分开考虑,它们间是否有明确的约束关系

    可以发现mp和sp的相关操作和在哪一个回合无关,而只与需要的回合总数有关

    那么就先DP出fm[i]和fs[i],分别表示用i个回合能用mp与sp打出的最大伤害,可以算出最少要mneed个回合战胜boss

    接下来再逐个回合考虑hp问题,用dp[i][j]表示到第i个回合血量为j时能挤出的最大回合数

    能挤出mneed个回合则YES,能在第n回合保持不死则输出Tie,否则NO

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int MAXN=1000+10;
    const int INF=1<<27;
    
    int T,n,m,x,hp,mp,sp,mp_cnt,sp_cnt,dhp,dsp,dmp;
    int a[MAXN],ump[MAXN],amp[MAXN],usp[MAXN],asp[MAXN];
    int fm[MAXN],gm[MAXN][MAXN],fs[MAXN],gs[MAXN][MAXN],dp[MAXN][MAXN];
    
    void up(int &x,int y){if(x<y) x=y;}
    
    void solve()
    {
        memset(fm,0,sizeof(fm));memset(gm,0,sizeof(gm));
        memset(fs,0,sizeof(fs));memset(gs,0,sizeof(gs));
        
        
        cin>>n>>m>>hp>>mp>>sp>>dhp>>dmp>>dsp>>x;
        for(int i=1;i<=n;i++) cin >> a[i];
        cin >> mp_cnt;
        for(int i=1;i<=mp_cnt;i++) cin >> ump[i] >> amp[i];
        cin >> sp_cnt;
        for(int i=1;i<=sp_cnt;i++) cin >> usp[i] >> asp[i];
        
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<=mp;j++) up(fm[i],gm[i][j]);
            if(i==n) break;
            for(int j=0;j<=mp;j++)
            {
                up(gm[i+1][min(mp,j+dmp)],gm[i][j]);
                for(int k=1;k<=mp_cnt;k++)
                    if(j>=ump[k]) up(gm[i+1][j-ump[k]],gm[i][j]+amp[k]);
            }
        }
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<=sp;j++) up(fs[i],gs[i][j]);
            if(i==n) break;
            for(int j=0;j<=sp;j++)
            {
                up(gs[i+1][min(sp,j+dsp)],gs[i][j]+x);
                for(int k=1;k<=sp_cnt;k++)
                    if(j>=usp[k]) up(gs[i+1][j-usp[k]],gs[i][j]+asp[k]);
            }
        }
        
        int mneed=INF;
        for(int i=0;i<=n;i++)
            for(int j=0;j<=n;j++)
                if(fm[i]+fs[j]>=m) mneed=min(mneed,i+j);
        
        for(int i=0;i<MAXN;i++)
            for(int j=0;j<MAXN;j++)
                dp[i][j]=-INF;
                
        dp[1][hp]=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=hp;j++)
                if(dp[i][j]>=mneed)
                {
                    cout << "Yes " << i << endl;
                    return;
                }
            for(int j=1;j<=hp;j++)
            {
                if(min(hp,j+dhp)>a[i]) up(dp[i+1][min(hp,j+dhp)-a[i]],dp[i][j]);
                if(j-a[i]>0) up(dp[i+1][j-a[i]],dp[i][j]+1);
            }
        }
        
        for(int i=1;i<=hp;i++)
            if(dp[n+1][i]>=0)
            {
                cout << "Tie" << endl;
                return;
            }
        cout << "No" << endl;
    }
    
    int main()
    {
        cin >> T;
        while(T--) solve();
        return 0;
    }

    Review:

    发现转移状态时要维护的信息过多时,可以查看这些量是否可以分离

    先逐个转移,最后再根据较弱的约束将状态加合

    注意大小写啊!!

  • 相关阅读:
    170110-学习MoveIt!
    12.29-ros-gazebo高级
    12.27-ros-gazebo基础
    4.9-Simulation in gazebo or webots
    6.28-机器人模拟器Gazebo基础
    4.8-URDF in ROS
    Win32双缓冲画图原理
    Win32 计时器
    最简化的DirectX 11开发环境的配置 VS2010
    VS2010 C++编译报错LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
  • 原文地址:https://www.cnblogs.com/newera/p/9050337.html
Copyright © 2020-2023  润新知