Diablo III ZOJ - 3769

题目链接 https://vjudge.net/problem/ZOJ-3769

代码转自:https://blog.csdn.net/Since_natural_ran/article/details/76037493?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171878667916800185844738%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171878667916800185844738&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-2-76037493-null-null.142v100control&utm_term=zoj3769

分组背包的变式。

题意:

给出13类装备,每种装备都有攻击力和防御值,每种装备只允许选择一个,13种装备里如果选择了双手装备,就不能选择武器和护盾了,还有戒指可以双手各带一个,求满足防御值至少在M的情况下,攻击力最大为多少

思路:

首先我们先哈希把13类装备对应到vector[]中

void init()
{
    mp["Weapon"] = 1;
    mp["Shield"] = 2;
    mp["Two-Handed"] = 3;
    mp["Finger"] = 4;
    mp["Feet"] = 5;
    mp["Legs"] = 6;
    mp["Waist"] = 7;
    mp["Wrist"] = 8;
    mp["Hand"] = 9;
    mp["Torso"] = 10;
    mp["Neck"] = 11;
    mp["Shoulder"] = 12;
    mp["Head"] = 13;
}

然后我们按照题意处理这些数据

 int length1 = G[1].size();
        int length2 = G[2].size();
        for(int j = 0;j < length2; j++) {
            G[3].push_back(G[2][j]);
        }
        for(int i = 0;i < length1; i++) {
            G[3].push_back(G[1][i]);
            for(int j = 0;j < length2; j++) {
                G[3].push_back((goods){G[1][i].Damage+G[2][j].Damage,G[1][i].Toughness+G[2][j].Toughness});
            }
        }
        int length3 = G[4].size();
        for(int i = 0;i < length3; i++) {
            for(int j = i + 1;j < length3; j++) {
                G[4].push_back((goods){G[4][i].Damage+G[4][j].Damage,G[4][i].Toughness+G[4][j].Toughness});
            }
        }

处理好了之后,就是一个分组背包。

不过直接写分组背包像我这样会TLE

设$dp[i][j]$表示前i个物品,韧度为j的最大攻击力,然后我算了一个韧度和,是1500000

int dp[15][1500000];
int ans = 0;
   for (int i = 4; i <= 14; i++) {
      for (int j = 1500000; j >= 0; j--) {
         for (auto [a, b] : v[i]) {
            if(j >= a) {
               dp[i][j] = max(dp[i - 1][j - a] + b, dp[i][j]);
            }
         }
      }
   }
   for (int i = M; i <= 1500000; i++) {
      ans = max(ans, dp[14][i]);
   }
   
   if(ans == 0) {
      cout << "-1\n";
   } else {
      cout << ans << "\n";
   }

这样写就会mle+tle

我们来看看正解优化的多妙。

memset(dp,-1,sizeof(dp));
        dp[2][0] = 0;
        for(int i = 3;i <= 13; i++) {
            for(int j = m;j >= 0; j--) {
                dp[i][j] = max(dp[i][j],dp[i-1][j]);    //更新装备状态,即使后边没有装备了,也能到到之前的状态。(必须步骤)
                if(dp[i-1][j] == - 1) continue;     //若要买下一个装备,那么上一个装备的状态一定要达到
                int length = G[i].size();
                for(int k = 0;k < length; k++) {
                    goods e = G[i][k];
                    int d = min(e.Toughness + j,m); //如果大于m则当做m即可(极妙)
                    dp[i][d] = max(dp[i][d],dp[i-1][j]+e.Damage);
                }
            }
        }
        cout<<dp[13][m]<<endl;

我觉得有两个地方很妙:

  • 他把第二维的上限设置成m,如果大于m等价于m,优化了空间,也优化了时间
  • 第二个是,这个题与平常的分组背包不同的是,分组背包是本金为m,能获得的最优价值是多少。这个题是只要用m去获得价值,所以题解是从前向后dp。我觉得是这样。
posted @ 2024-06-19 17:20  contiguous  阅读(9)  评论(0编辑  收藏  举报