51nod 算法马拉松11 A 翻硬币

理论解法转自知乎,原文链接:http://www.zhihu.com/question/26570175/answer/33312310

题目链接:http://www.51nod.com/contest/problem.html#!problemId=1613


【有 n 个硬币,一开始全部正面朝上,每次可以翻转 k 个硬币( k 小于 n ),那么至少要 p 次翻转,才能让所有硬币反面朝上,求 p 的值】

高能预警:
本题所用的数学工具不超过初中毕业水平,但情况繁多,完整讨论并不容易,结论写在最后。

解:


本题的精髓在于对奇偶性的讨论。

——情况1:若 n 为奇数——

1.1 若 k 为偶数 => 无解

证明


若要让所有硬币最终翻面,则每个硬币都要翻面奇数次,共有奇数个硬币,所以所有硬币的翻面总数为奇数,但每次只能翻面偶数个硬币,显然不可能。证毕!

1.2 若 k 为奇数 => p为不小于 n/k 的最小奇数

(例1:n=7,k=5,那么 n/k =1.4 则 p=3 )
(例2:n=25,k=7,那么 n/k= 25/7 则 p=5)

证明

必要性
若要让所有硬币最终翻面,则每个硬币都要翻面奇数次,共有奇数个硬币,所以所有硬币的翻面总数为奇数,而每次只能翻奇数个硬币,所以总的翻转次数必然是奇数次,而翻转次数不到 n/k 次时,并不能使所有硬币至少翻面1次,所以p至少是不小于 n/k 的最小 奇数

充分性:
  • k=n-2 时,只要3次翻转即可
给硬币编号为1-n,
第1次翻转编号:1~n-2
第2次翻转编号:1~n-3、n-1
第3次翻转编号:1~n-3、n
Done!(前 n-3 个硬币翻面 3 次,后 3 个翻面 1 次)

  • \frac{n}{3}\leq k<n-2 时,依然只要3次翻转
只要让前面的 \frac{3k-n}{2} 个硬币翻转 3 次,后面的 \frac{3n-3k}{2} 个硬币翻转 1 次即可。这是显然可以做到的。

  • k<\frac{n}{3}时,需要p次翻转(p为不小于 n/k 的最小奇数)
根据定义, pk\geq n, (p-2)k<n,由于 k<n,所以 pk<3n 这意味者翻转 p 次后,平均来说,每个硬币翻面次数小于3次。只要让前面的 \frac{pk-n}{2} 个硬币翻转 3 次,后面的 \frac{3n-pk}{2} 个硬币翻转 1 次即可。

——情况2:若 n 为偶数——


2.1 若 \frac{n}{2}<k<n-1 且为偶数=> p=3


只要让前面的 \frac{3k-n}{2} 个硬币翻转 3 次,后面的 \frac{3n-3k}{2} 个硬币翻转 1 次即可。
这个情况,和 n 为奇数是类似的。

2.2 若 \frac{n}{2}<k< n-1 且为奇数=>p为不小于 n/(n-k) 的最小偶数

这种情况比较特殊。
首先由奇偶性得出翻转次数必为偶数,而每一枚硬币翻转的次数为奇数,则每一枚硬币至少不翻转 1 次。
其次,每次有 n-k 枚不翻转,所以 p 必须不小于 n/(n-k) 。
方案:让前面 \frac{kp-np+3n}{2} 硬币翻转 p-1 次,后面 \frac{np-kp-n}{2} 硬币翻转 p-3 次

2.3 若k\leq \frac{n}{2} 且为偶数=> p为不小于 n/k 的最小整数

k=\frac{n}{2} 时,显然 p=2;
反之,只要让前面的 \frac{pk-n}{2} 个硬币翻转 3 次,后面的 \frac{3n-pk}{2} 个硬币翻转 1 次即可。

2.4 若 k\leq \frac{n}{2} 且为奇数=> p为不小于 n/k 的最小偶数

k=\frac{n}{2} 时,显然 p=2;
反之,首先由奇偶性得出翻转次数必为偶数,
只要让前面的 \frac{pk-n}{2} 个硬币翻转 3 次,后面的 \frac{3n-pk}{2} 个硬币翻转 1 次即可。

————————————

综上所述:

若 n 为奇数:

  • 若 k 为偶数 \Rightarrow 无解
  • 若 k 为奇数 \Rightarrow p 为不小于 n/k 的最小奇数
若 n 为偶数:
  • 若 k为偶数,且\frac{n}{2}<k<n-1 \Rightarrow p=3
  • 若 k为奇数,且\frac{n}{2}<k\leq n-1 \Rightarrow p 为不小于 n/(n-k) 的最小偶数
  • 若 k为偶数,且k\leq \frac{n}{2} \Rightarrow p 为不小于 n/k 的最小整数
  • 若 k为奇数,且k\leq \frac{n}{2} \Rightarrow p 为不小于 n/k 的最小偶数
// 翻硬币.cpp
#include <bits/stdc++.h>
#define LL long long
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
#define PI 3.1415926535897932626
#define EXIT exit(0);
#define PAUSE system("pause");
#define DEBUG puts("Here is a BUG");
#define SYNC_CLOSE ios::sync_with_stdio(false);
#define what_is(x) cout << #x << " is " << x << endl;
#define CLEAR(name, init) memset(name, init, sizeof(name));
const double eps = 1e-8;
const int MAXN = (int)1e9 + 5;
using namespace std;

int solve(int n, int k) {
    if (n & 1) {
        if (k & 1) {
            int tmp = n/k + bool(n % k);
            tmp += tmp % 2 == 0;
            return tmp;
        }
        return -1;
    }
    if (k == n - 1) return n;
    if (k*2 == n) return 2;
    if (k % 2 == 0) {
        if (k > n / 2) return 3;
        return n/k + bool(n % k);
    }
    if (k > n / 2) {
        k = n - k;
        int tmp = n/k + bool(n % k);
        tmp += tmp % 2;
        return tmp;
    }
    int tmp = n/k + bool(n % k);
    tmp += tmp % 2;
    return tmp;
}

int main(int argc, char const *argv[]) {
#ifndef ONLINE_JUDGE
    freopen("D:\\Documents\\Disk_Synchronous\\Programs\\Acm\\input.txt", "r", stdin);
#endif
    int n, k;
    cin >> n >> k;
    cout << solve(n, k) << endl;
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值