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. C# 注册DLL至GAC 并在添加引用中使用该DLL

    本文链接:https://blog.csdn.net/World3000/article/details/819835901,创建一个强签名的类库 类库属性中新建秘钥文件 或者使用工具 使用命令sn ...

  2. 函数vs方法

    举例 python代码示例: class A(): def method_demo(): print("我是一个方法,在类内") def function_demo(): prin ...

  3. Unreal Engine 4 中的 UI 优化技巧

    转自:https://mp.weixin.qq.com/s/bybEHM9tF-jBPxxqXfrPOQ## Unreal Open Day 2017 活动上 Epic Games 开发者支持工程师郭 ...

  4. 算法练习之x的平方根,爬楼梯,删除排序链表中的重复元素, 合并两个有序数组

    1.x的平方根 java (1)直接使用函数 class Solution { public int mySqrt(int x) { int rs = 0; rs = (int)Math.sqrt(x ...

  5. 根据本地/服务器时间获取指定时区时间 new Date指定时区时间

    1.代码 function getTimeByTimeZone(timeZone){ var d=new Date(); localTime = d.getTime(), localOffset=d. ...

  6. Linux定时自动备份oracle数据库

    1.在服务器创建备份目录,并赋予权限mkdir -p /backup/P_DATA #新建shell脚本存放路径mkdir -p /backup/P_DB_BACK  #新建Oracle数据库备份目录 ...

  7. dockfile构建自己的tomcat

    touch  Dockerfile 在Dockerfile中输入以下内容 FROM centosMAINTAINER Irish<3395327965@qq.com>#把宿主机当前上下文的 ...

  8. Linux下创建Oracle19C的数据库实例

    接上一篇博客,安装 Oracle19chttps://www.cnblogs.com/xuzhaoyang/p/11264557.html 切换到root用户下,切换目录到db_1,执行,遇到选择路径 ...

  9. 路由Routers

    路由Routers 对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用Routers来帮助我们快速实现路由信息. REST framework提供 ...

  10. int and Integer

    int和Integer的区别 1.Integer是int的包装类,int则是java的一种基本数据类型 2.Integer变量必须实例化后才能使用,而int变量不需要 3.Integer实际是对象的引 ...