牛客练习赛78 B.CCA的搬运(贪心、模拟)

题目:

在一个竖直的洞里有 n 个有重量的球,需要进行 m 次操作,每次操作需要将其中一个球拿出来然后放在最上面 。
取出一个小球放在最上面需要消耗的体力为它上面的小球的重量之和 。
现在给定每次操作需要取 的小球的编号,要求出一种初始的放球方案使得消耗的总体力最少 。
输入描述:
第一行两个正整数 n 和 m,意义如题所示 。
第二行 n 个正整数,分别表示 n 个球的重量 。
第三行 m 个正整数,分别表示 m 次操作取出小球的编号 。

输出描述:
一个整数表示消耗的总体力的最小值 。

输入
3 3
1 2 3
3 2 1

输出
8

备注:
n,m <= 2000,1 <= 每个小球的重量 <= 100 。

解析:这道题一开始勿以为所有的小球都要进行一次操作,并且 n = m, 但实际并非如此,并且可能对某个小球没有任何操作。
大概思路:贪心 + 模拟

  • 贪心:可以通过找规律发现,假设当前仅有两个小球A、B,必须按取球的顺序从上到下摆放(与A、B重量无关),假设我们与结论相反:此时要求先取A后取B,我们把B摆放在A之上,那么体力:A + B,反之体力为:A。若仅取一次或大于两次结论一样,在这不在赘述。
    换言之,我们需要将小球按照第一次操作时的顺序进行从上到下摆放。
  • 模拟:每次体力求和时,需要将当前操作的小球上方的小球体力进行求和,并且将上方所有小球往下移动一个位置,将该操作小球放置最上方。
  • ps:假设有个球架放小球吧,bp[球架的位置] = 小球编号,p[小球编号] = 球架位置,利用这两个数组来模拟小球实际移动过程

代码:

#include<iostream>
using namespace std;
const int N = 2005;
int n, m, cnt = 0;
int w[N], a[N]; //球重量、球编号
int bp[N], p[N]; //从上往下第i个位置存放球的编号、第x个球所摆放在的位置(顶部为1)
int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        cin >> w[i];
    for(int i = 1; i <= m; i++)
    {
        cin >> a[i];
        if(!p[a[i]]) //判断之前是否已记录该小球存放的位置
        {
            p[a[i]] = ++cnt;
            bp[cnt] = a[i];
        }
    }
    int res = 0;
    for(int i = 1; i <= m; i++)
    {
        for(int j = p[a[i]] - 1; j >= 1; j--) //从下往上进行小球重量相加; j:表示球架位置(某一层)
        {
            res += w[bp[j]]; //通过球的编号查找到球的重量
            p[bp[j]] ++; //该小球所在球架位置需要进行更新
            bp[j+1] = bp[j]; //球架下一层放的球需要进行更新
        }
        //将当前操作小球放置顶部
        bp[1] = a[i];
        p[a[i]] = 1;
    }
    cout << res << endl;
    return 0;
}

posted @ 2021-03-14 17:34  ~K2MnO4  阅读(106)  评论(0)    收藏  举报