1320:【例6.2】均分纸牌(Noip2002)

时间限制: 1000 ms         内存限制: 65536 KB
提交数: 3537     通过数: 1839

【题目描述】

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

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

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

例如 n=4,4堆纸牌数分别为:  ① 9 ② 8 ③ 17 ④ 6

移动3次可达到目的:

从 ③ 取4张牌放到④(9 8 13 10)->从③取3张牌放到 ②(9 11 10 10)-> 从②取1张牌放到①(10 10 10 10)。

【输入】

n(n 堆纸牌,1 ≤ n ≤ 100)

a1 a2 … an (n 堆纸牌,每堆纸牌初始数,l≤ ai ≤10000)。

【输出】

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

【输入样例】

4
9 8 17 6

【输出样例】

3

例题不怎么详的解:
前辈们告诉我们,OI的很多题目想要解出来是需要很多奇巧淫技的,多积累点奇怪的思路和技巧,不仅对提升成绩有帮助,还对自己大脑开发有好处(一本正经)。 实际上很多OI题都要靠奇葩的技巧,就像其他学科竞赛那样。。。
这一题也是如此。
很多人一上来看到这题目就直接懵逼了,没有头绪,难不成让我用搜索做?
于是乎玄乎的来了,我们代入一点逆向的思想,分析输入数据和输出数据的关系,震惊地发现原来所有牌堆中牌的总数平分到每一个堆中(即平均值)就是最后均分的结果,也照应了题目。
好吧我承认这很容易想出来,因为题目给出的暗示很明显:

现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。
好的,我实力眼瞎没看到,刚看到这道题我差点就去搜索了。。。所以说啊,认真看题无论在哪里都是第一要素!!! 算法分析:
回到正题,给出的n堆纸牌中,由于分配不均,肯定存在牌数最多的一堆,我们就从这里开始。经过简单分析,因为题目只允许将中间牌堆上的牌放到左右两边,得出以下参考思路: 我们将所有牌的数量除以总堆数的结果称为平均值;
我们将某一堆上牌的总数与平均值之差称为需求值; 找到牌数最多的牌堆,将牌分成两部分,一部分的数量是该堆左边每堆牌需求值总和,另一部分的数量是右边每堆牌需求值总和,然后分别放置到左右两边。
向左右两边继续执行这样的操作,由于已经执行过的部分已经达到平均值,无需改变,所以这种算法具有无后效性,只需将最中间的牌向两边摊开就行。 这时我突然意识到一个问题,如果用这种思路设计算法,那么代码设计会相对繁琐,因为要实现规划左右两段的功能,于是竞赛书上的写法使我虎躯一震,真的没想到还有这种操作,果然竞赛靠的还是一个巧。
那就是: 假设一组牌堆,3 7 17 13,写成需求值的形式是-7 -3 7 3,我们把 -7 移到 -3 里去,把 -7 变成 0,-3 变成 -10(相当于-3那一堆挪7张牌到-7那一堆,但是可以在算法中写成此种形式),
-10 再移到 7 里去,7 变成 -3,最后把 -3 移到 3 里去,大功告成。 很明显这种思路比我的思路要来的简单一些,代码量更少,那么我就只分析这种算法(其实是我懒)。 设置一个关键数组a,储存所有牌堆的牌数。
设置一个变量avg,储存均值。
设置一个变量tot,储存移动总步骤。
我们首先要将需求值计算出来放进一个数组,我嫌麻烦,所以将数组a再次利用了一次,将需求值覆盖原数组。
接下来就是算法核心部分的编写,我们需要遍历一遍数组,执行上面那个思路:
for(int t=i;t<j;t++)
{
a[t+1]+=a[t];
a[t]=0;
tot++;
}

 这就是核心代码,很简单,不过有些细节还要处理。

注意:要 素 察 觉,如果开头和结尾的牌堆的需求值为0,那么此牌堆无需移动。正确姿势应该是从第一个需求值不为0的牌堆移动到最后一个需求值不为0的牌堆。
而且,在移动过程中如果出现恰好前后两牌堆需求值合并得到的值为0的情况,可以跳过一步,相当于遍历到此牌堆时,前面的需求值恰好抵消,等同于已经完成了一轮均分。
因此,现在需求值为0的那个牌堆不需要再移动。
得到如下代码:
      i=0;j=n-1;
while(a[i]==0&&i<n) i++;
while(a[j]==0&&j>1) j--;
for(int t=i;t<j;t++)
{
if(a[t]==0) continue;
a[t+1]+=a[t];
a[t]=0;
tot++;
}
样例代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
int main()
{
int avg=0,n,a[101],i,tot=0,j;
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
avg+=a[i];
}
avg/=i;
for(j=0;j<n;j++) a[j]-=avg;
i=0;j=n-1;
while(a[i]==0&&i<n) i++;
while(a[j]==0&&j>1) j--;
for(int t=i;t<j;t++)
{
if(a[t]==0) continue;
a[t+1]+=a[t];
a[t]=0;
tot++;
}
printf("%d",tot);
return 0;
}

  2019-02-08 19:36:03

 

均分纸牌(Noip2002)的更多相关文章

  1. 洛谷-均分纸牌-NOIP2002提高组复赛

    题目描述 Description 有 N 堆纸牌,编号分别为 1,2,…, N.每堆上有若干张,但纸牌总数必为 N 的倍数.可以在任一堆上取若于张纸牌,然后移动. 移牌规则为:在编号为 1 堆上取的纸 ...

  2. 贪心训练均分纸牌Noip2002

    题目链接:https://www.luogu.com.cn/problem/P1031 题目描述 有N堆纸牌,编号分别为 1,2,…,N.每堆上有若干张,但纸牌总数必为N的倍数.可以在任一堆上取若干张 ...

  3. NOIP2002 均分纸牌

    题一 均分纸牌 (存盘名: NOIPG1) [问题描述] 有 N 堆纸牌,编号分别为 1,2,…, N.每堆上有若干张,但纸牌总数必为 N 的倍数.可以在任一堆上取若于张纸牌,然后移动. 移牌规则为: ...

  4. code vs 1098 均分纸牌(贪心)

    1098 均分纸牌 2002年NOIP全国联赛提高组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解   题目描述 Description 有 N 堆纸牌 ...

  5. NOIP200205均分纸牌

                                                                  均分纸牌 描述 有 N 堆纸牌,编号分别为 1,2,…, N.每堆上有若干张 ...

  6. wikioi 1098 均分纸牌

    题目描述 Description 有 N 堆纸牌,编号分别为 1,2,-, N.每堆上有若干张,但纸牌总数必为 N 的倍数.可以在任一堆上取若于张纸牌,然后移动. 移牌规则为:在编号为 1 堆上取的纸 ...

  7. 【洛谷p1031】均分纸牌

    [博客园的第一条随笔,值得纪念一下] 均分纸牌[传送门] 洛谷上的算法标签是 这道题是一道贪心题,过了四遍才过(蒟蒻有点废) 第一遍的时候考虑的非常少,只想到了求出平均数→求差值→从左往右加差值: 这 ...

  8. 洛谷P1368 均分纸牌(加强版)

    P1368 均分纸牌(加强版) 题目描述 有 N 堆纸牌,编号分别为 1,2,…, N.每堆上有若干张,纸牌总数必为 N 的倍数.可以在任一堆上取1张纸牌,然后移动. 移牌规则为:在编号为 1 堆上取 ...

  9. 洛谷P1031 均分纸牌

    P1031 均分纸牌 题目描述 有 N 堆纸牌,编号分别为 1,2,…, N.每堆上有若干张,但纸牌总数必为 N 的倍数.可以在任一堆上取若干张纸牌,然后移动. 移牌规则为:在编号为 1 堆上取的纸牌 ...

随机推荐

  1. 【视频开发】RTSP SERVER(基于live555)详细设计

    /* *本文基于LIVE555的嵌入式的RTSP流媒体服务器一个设计文档,个中细节现剖于此,有需者可参考指正,同时也方便后期自己查阅.(本版本是基于2011年的live555) 作者:llf_17@q ...

  2. C/C++ 面试-面向对象的特性

    面向对象的三大特性 此文只是简单介绍一下三大特性   详细介绍在: 封装:http://www.cnblogs.com/52why/p/7631381.html 继承:http://www.cnblo ...

  3. 修改mysql自增字段的方法

    修改mysql自增字段的方法 修改 test_user 库 user 表 auto_increment为 10000(从10000开始递增) <pre>mysql> alter ta ...

  4. java并发-ReentrantLock的lock和lockInterruptibly的区别

    ReentrantLock的加锁方法Lock()提供了无条件地轮询获取锁的方式,lockInterruptibly()提供了可中断的锁获取方式.这两个方法的区别在哪里呢?通过分析源码可以知道lock方 ...

  5. 学习数据结构Day4

    链表 之前看过了动态数组,栈和队列,虽然我们把第一个叫做动态数组,但是,他们的底层实质上还是静态数组.靠 resize来实现动态数组.而链表是真正的数据结构 链表需要一个节点. 数据存储在链表中 相当 ...

  6. Echarts数据可视化grid直角坐标系(xAxis、yAxis)

    mytextStyle={ color:"#333", //文字颜色 fontStyle:"normal", //italic斜体 oblique倾斜 font ...

  7. Prometheus入门到放弃(6)之AlertManager进阶

    前面几个篇幅,我们介绍了alertmanger报警配置,在实际运维过程中,我们都会遇到,报警的重复发送,以及报警信息关联性报警.接下来我们就介绍下通过alertmanger对告警信息的收敛.一.告警分 ...

  8. 封装关于金额计算的double工具类

    由于直接使用double类型的加减乘除,可能会出现不可预测的问题,精度丢失等等.在业务中,计算金额是一件很重要的事情. 可以直接使用BigDecimal类,进行加减乘除.相关BigDecimal类介绍 ...

  9. 「CTS2019」氪金手游

    「CTS2019」氪金手游 解题思路 考场上想出了外向树的做法,居然没意识到反向边可以容斥,其实外向树会做的话这个题差不多就做完了. 令 \(dp[u][i]\) 表示单独考虑 \(u\) 节点所在子 ...

  10. java之struts2之数据检验

    1.使用struts2时,有时候需要对数据进行相关的验证.如果对数据的要求比较严格,或对安全性要求比较高时,前端 js 验证还不够, 需要在后端再进行一次验证,保证数据的安全性. 2.struts2提 ...