2018 CodeM初赛A轮 下棋答案(B题) (第二道题)

题目:

      有一个1*n的棋盘, 上面有若干个棋子,一个格子上可能有多个棋子,

      你每次操作是先选择一个棋子,然后选择一下两个操作中的一个:

           (1)若该棋子不在(1,1),让这个棋子往左走一格,即从(1,x)走到(1,x - 1);

          (2)若该棋子不在(1,n),且这个棋子曾静到达过(1,1),让这个格子往右走一格,即从(1,x)走到(1,x+1)。

     给定一开始每个各自上有几个棋子,再给定目标每个格子上上需要几个棋子,求最少需要多少次操作。

     输入:

         第一行一个正整数n表示棋盘大小。

         第二行n个非负整数a_1,a_2,。。。,a_n 表示一开始(1,i)上有几个棋子。

         第三行n个非负整数b_1,b_2,。。。,b_n 表示目标局面里的(1,i)上有几个棋子。

     输出:

        输出一个非负整数,表示最少需要几次操作。

例子:

     输入:

        5

        0  0  1  1  0

        1  0  0  0  1

     输出:

       9


题目分析:

  典型的贪心法问题,目标数组减去起始数组,得到的向量中,如果元素为负,表示拥有多余的棋子;如果元素为正,表示,需要额外棋子,根据操作的定义,我们可以想到最有的策略是:棋盘中最右边的多余的棋子,给最左边需要的额外的棋子,坐标的相减即为所需的操作次数,而遇到在多余棋子坐标右边的需要的棋子,就需要向左移到头,再向右移,所需的操作为两个点的坐标相加。

贪心总结三点:

  1、相差为0的元素,代表不移动

  2、目标需要棋子的坐标如果在含有棋子的坐标的右边,则需要移动到最左边,然后再向右移动

  3、目标需要棋子的坐标如果在含有棋子的坐标的左边,则只需要向左移动相应格子即可

而我们的贪心算法要尽可能是第三点的操作最多,则可满足题意。

  代码

  代码核心:定义两个队列,一个队列是拥有多余棋子的队列(这个队列是从右向左入队),另一个是需要棋子的队列(这个队列是从左向右入队),根据最有策略,列队入队的方向有所不同,注意队列还要保存坐标信息,当队首的棋子数为零则出队(这里的棋子数包括所需的棋子数或是多余的棋子数)。第二步就需要判断位置信息,利用位置来确定操作次数。代码如下:

//

//  main.cpp

//  codeM初赛2

//

//  Created by Mr Gao on 2018/6/9.

//  Copyright © 2018年 Mr Gao. All rights reserved.

//


#include <iostream>

#include <vector>

#include <queue>

using namespace std;

int main(int argc, const char * argv[]) {

    // insert code here...

    int n;

    cin>>n;

    if(n == 0)

        cout<<0<<endl;

    vector<long long> start(n);

    vector<long long> end(n);

    for(int i = 0; i < n; i++)

        cin>>start[i];

    for(int i = 0; i < n; i++)

        cin>>end[i];

    queue<pair<long long,int>> hasPiece;

    queue<pair<long long,int>> needPiece;

    for(int i = 0; i < n; i++){

        long long diff = end[i] - start[i];

        if(diff > 0)

            needPiece.push(pair<long long,int>(diff, i));

    }

    for(int i = n - 1; i >= 0; i--){

        long long diff = end[i] - start[i];

        if(diff < 0)

            hasPiece.push(pair<long long,int>(-diff, i));

    }

    long long ret = 0;

    while(!hasPiece.empty()){

        if(hasPiece.front().first >= needPiece.front().first && hasPiece.front().second > needPiece.front().second){

            ret += needPiece.front().first * (hasPiece.front().second - needPiece.front().second);

            hasPiece.front().first -= needPiece.front().first;

            needPiece.front().first = 0LL;

        }

        else if(hasPiece.front().first < needPiece.front().first && hasPiece.front().second > needPiece.front().second){

            ret += hasPiece.front().first * (hasPiece.front().second - needPiece.front().second);

            needPiece.front().first -= hasPiece.front().first;

            hasPiece.front().first = 0LL;

        }

        else  if(hasPiece.front().first >= needPiece.front().first && hasPiece.front().second < needPiece.front().second){

            ret += needPiece.front().first * (needPiece.front().second + hasPiece.front().second);

            hasPiece.front().first -= needPiece.front().first;

            needPiece.front().first = 0LL;

        }

        else if(hasPiece.front().first < needPiece.front().first && hasPiece.front().second < needPiece.front().second){

            ret += hasPiece.front().first * (needPiece.front().second + hasPiece.front().second);

            needPiece.front().first -= hasPiece.front().first;

            hasPiece.front().first = 0LL;

        }

        if(hasPiece.front().first == 0LL)

            hasPiece.pop();

        if(needPiece.front().first == 0LL)

            needPiece.pop();

    }

    cout<<ret<<endl;

    return 0;

}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值