跳转至

洛谷-P1031 均分纸牌(贪心)

题目描述

有NN堆纸牌,编号分别为 1,2,…,N。每堆上有若干张,但纸牌总数必为N的倍数。可以在任一堆上取若干张纸牌,然后移动。

移牌规则为:在编号为1堆上取的纸牌,只能移到编号为2的堆上;在编号为N的堆上取的纸牌,只能移到编号为N-1的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。

例如N=4,4堆纸牌数分别为:

①9②8③17④6

移动33次可达到目的:

从 ③ 取44张牌放到 ④ (9,8,13,10)-> 从 ③ 取33张牌放到 ②(9,11,10,10)-> 从 ② 取11张牌放到①(10,10,10,10)。

输入格式

两行

第一行为:N(N 堆纸牌,1≤N≤100)

第二行为:A_,A_2, … ,A_n (N堆纸牌,每堆纸牌初始数,1≤Ai≤10000)

输出格式

一行:即所有堆均达到相等时的最少移动次数。

输入输出样例

输入 #1

4
9 8 17 6

输出 #1

3

#include <iostream>
#include <iomanip>
#include <vector>
#include <cmath>
#include <string>
#include <climits>
#include <cstdio>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <set>
#include <algorithm>

using namespace std;

int n;
vector<int> seq(1005);
int average = 0;

int solve()
{
    int cnt = 0;
    for (int i = 0; i < n - 1; ++i) {
        if (seq[i] == average) continue;

        if (seq[i] < average) {
            seq[i + 1] -= average - seq[i];
            seq[i] = average;
        }
        else {
            seq[i + 1] += seq[i] - average;
            seq[i] = average;
        }
        ++cnt;
    }

    return cnt;
}

int main()
{
    std::ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    cin >> n;
    int sum = 0;
    for (int i = 0; i < n; ++i) {
        cin >> seq[i];
        sum += seq[i];
    }
    average = sum / n;

    cout << solve() << endl;

    return 0;
}

贪心类的问题很难说有一个固定的模型,最好是自己举几个例子找找规律,看人为选择怎么去解决,然后用算法去描述这个行为。

这道题很类似开关灯的问题,应该去像一个办法,让后续的结果不影响前面的结果。因为让所有部分都平均,给出的例子,可以认为是17分给6四个,分为8两个,9一个,因为不允许跨越传递,所以可以认为是8的部分暂时帮9的部分存储了。那么用程序来描述,可以认为是打欠条的过程,比如第一个9,自身不够10,那么就向后面一个数借1,并打个欠条,第二个8缺2,本来只需要向17借2,但是手里有个欠条,于是告诉17再多给1个,分给9的直接给8就好了,就可以还清欠条。