CodeVs 1098 均分纸牌

摘要:介绍关于纸牌均分题目算法第一种自己原创,另一种是网上普遍算法。

题目:                                                                                          

题目描述 Description

N 堆纸牌,编号分别为 12…, N。每堆上有若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若于张纸牌,然后移动。
  移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 的堆上;在编号为 N 的堆上取的纸牌,只能移到编号为 N-1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。
  现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。

  例如 N=44 堆纸牌数分别为:
   9  8  17  6
  移动3次可达到目的:
  从 4 张牌放到9 8 13 10 -> 3 张牌放到9 11 10 10-> 1 张牌放到10 10 10 10)。

输入描述 Input Description

第一行NN 堆纸牌,1 <= N <= 100
第二行A1 A2 … An N 堆纸牌,每堆纸牌初始数,l<= Ai <=10000

输出描述 Output Description

输出至屏幕。格式为:
所有堆均达到相等时的最少移动次数。

样例输入 Sample Input

4
9 8 17 6

 

 

解题思路:                                                               

 

移牌规则,决定了大于平均堆牌数的堆必须至少要进行一次分牌把其多余的牌数分出去,分法有三种:分给左边、分给右边、两边分。

第一种,可能出现右边少了,需要将左边的牌通过H分发的情况,此时多出两次,即多了3次分发操作。第二种类似第一种。

第三种,先计算出两边需要分发的需求,需要多少分发多少。

显然应采取第三种分发策略。

 

子问题解决方法得到:每次将一个最大的堆按两边需求,向两边分发。

 

 

解题步骤:                                                               

1.     得最大堆的编号vector<int>::iterator iter

2.    计算左右需求divLeftdivRight,按需要分发。分发一次计数器++

3.     goto第一步,如果最大编号的值为平均值,表示分发完成。

 

 

#include<iostream>

#include<vector>

#include<algorithm>

#include<numeric>

usingnamespace std;

 

int main(){

   /*测试数据*/

    //int n = 4;

    //vector<int> v{ 9, 8, 17, 6 };

  

  /*输入数据,保存入vector v中*/

    int n = 6;

    int m;

    vector<int> v;

    cin >> n;

    while (cin >> m)

    {

       v.push_back(m);

    }

 

    /*解题*/

  int avg = accumulate(v.begin(), v.end(), 0) / n;//average平均值

 

    vector<int>::iterator iter = max_element(v.begin(), v.end());//从第一个最大的数开始移动

    int count = 0;//计数,每移动一次,++

    while (*iter >avg)//知道最大一个数位平均数为止,表示分堆完毕。否则进行分堆

    {

       int numsLeft = iter - v.begin();

       int numsRight = v.end() - iter - 1;

   

       int divLeft = numsLeft*avg - accumulate(v.begin(), iter, 0);

       int divRight = numsRight*avg - accumulate(iter + 1, v.end(), 0);

 

       if (divRight > 0){//左边需要移动

           *(iter + 1) += divRight;

           *iter -= divRight;

           count++;

       }

       if (divLeft > 0){//右边需要移动

           *(iter - 1) += divLeft;

           *iter -= divLeft;

           count++;

       }

      

       iter = max_element(v.begin(), v.end());//获取下一个最大的数

    }

 

   /*输出结果*/

    cout << count;

    return 0;

}

 

 

其他方法(即网上普遍方法):                                                                  

      

思路:

从第一堆到最后一堆,通过其右邻的堆(i+1),依此将当前堆i设为均堆,如果当前堆已经为均堆,堆编号++

主要步骤:

1. 计算右邻堆需要牌数      div = a[i] - average;

2. 右邻堆根据div正负,减牌或者加牌。a[i+1] += div;

主要代码:

 

if (v[i] != avg)//是否为均堆

       {

           div = v[i] - avg;//计算右邻堆需要牌数,为正表示分给右邻堆,为负表示从右邻堆得到。

           v[i] = avg;

           v[i + 1] += div;

           step++;

       }

 

 

/*problem:纸牌均分http://codevs.cn/problem/1098/

**author:shaoning.ding

**time:2015.5.1

*/

#include<iostream>

#include  <vector>

#include<numeric>

 

usingnamespace std;

int main()

{

    /*测试数据*/

    int n = 4;

    vector<int> v{ 9, 8, 17, 6 };

 

    /*input数据,保存vector v*/

    //int n,int m;

    //vector<int> v;

    //cin >> n;

    //while (cin >> m)

    //{

    //  v.push_back(m);

    //}

 

    /*解题*/

    int avg = accumulate(v.begin(), v.end(), 0) / n;//avg平均值

 

    int step = 0;

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

    {

       int div = 0;

        if (v[i] != avg)//是否为均堆

       {

           div = v[i] - avg;//计算右邻堆需要牌数,为正表示分给右邻堆,为负表示从右邻堆得到。

           v[i] = avg;

           v[i + 1] += div;

           step++;

       }

    }

   

    /*输出结果*/

    cout << step;

}

 

原文地址:https://www.cnblogs.com/dingblog/p/4470773.html