51nod 1613 翻硬币

来源:互联网 发布:程序员算法是什么 编辑:程序博客网 时间:2024/06/06 07:33

51nod 1613 翻硬币

有 n 个硬币,一开始全部正面朝上,
每次可以翻转 k 个硬币( k 小 于 n ),
那么至少要 p 次翻转,才能让所有硬币反面朝上,
求 p 的值。如果不能成功翻转则输出-1

这个题目 重点是对已有思路的想法的梳理
题目中 是要求 每次都要反转k个硬币。
我们每次反转 k个硬币,并通过一部分重叠 。
来凑出恰好k个硬币正面朝上。
也就是这一次反转的k个硬币可能会有上一次已经反转过的硬币。
一个硬币最多被反转两次,
那么每重复反转了一个硬币,就会比 不重复反转的时候多出来两个
没有反转的硬币。
例如 5个硬币 每次反转2个
开始的时候

00000

连续反转两次,两次没有重叠

11110

连续反转两次,有一个重叠

10100

有一个重叠的时候 ,因为出现了一次重叠,那么被反转的区间减小1,
同时,重叠部分反转回去,所以反面朝上的硬币数量比上面小了2
这个减小的数量 正好是重叠数量的2倍关系,也为我们凑出合适的区间,
埋下伏笔。

n=tk+rr=n%k

如果r==0 则有answer=t

所以下面的r都是大于0的

当t>1 时 我们是可以用重叠一部分的思路来搞定这个问题

但 t==1 时 因为只能有一个完整k连续的硬币,所以要分类讨论了

t>1时 :

设 x是重复反转的硬币数量
那么我们想要取一个恰当的 x使得

2x+r=k

很明显 当 r与k的奇偶性相同时 这样的x必然存在
同时 我们至少要反转

t+1

次才可以完全反转所有硬币 所以 此时答案就是

answer=t+1

当r为奇数,k为偶数时

这种情况是 无解的

我们设 第i个硬币反转

qi(qi)

次,则有:

pk=i=1nqi

但是因为pk为偶数 ,n为奇数,ni=1qi 为奇数,无解

当r为偶数,k为奇数时

证明 answer>t+1

因为 answer>t所以 answer>=t+1
answer==t+1 则必有一部分硬币被翻动超过1次
因为 此时我们多反转了 kr次 又因为kr是一个奇数
而被翻动超过一次的硬币必须增加偶数次反转
所以answer!=t+1
所以 answer>t+1

证明 对于 k+r个硬币,k为奇数,r为偶数,(如上文前提)

只需要 3次操作

当我们连续两次反转的时候,第二次反转k个硬币中,有A个是上一次反转的
那么这个时候,真正被反转的,只有:

2(kA)

这是一个偶数,同时有

0<=2(kA)<=2r

所以我们可以使用这种连续两次反转的方法 来吧其中r个硬币反转
同时 剩余的k个硬币只需要一次反转
总计 3次
那么对于

n=tk+r

反转次数为

t1+3=t+2

因为 answer>t+1,
所以answer=t+2;

t=1

此时就是 n=k+r的情况
此时t>1的情形中 的部分结论也是可以拿来直接用的
如 r为奇数,k为偶数时 无解
又如 r为偶数时 answer=3
我们梳理一下会有
当r为偶数时:answer=3
当r为奇数时,k为偶数时:无解
当r为奇数,k为奇数时:这种情况再讨论

也就是说 我们现在仅仅只有

r为奇数,k为奇数时是未知的。

那么我们讨论这种情况。
此时 n是偶数,之前我们说 连续两次反转可以完成

0<=2(kA)<=2r

个硬币的反转,也就是说我们用2(kA)来替代k,这样问题就可以规约到 r为偶数 k为偶数的情况
现在证明,这种方法得到的answer是最小的
依然有前提 k与r均为奇数
因为 n为 偶数 所以有

pk=i=1nqi    

所以,p为偶数,那么问题等价于每次的操作都是连续两次的反转。
所以用任意恰当的数字2a(2a<=2r)来替代k得到的 答案

answer=2(n2r+[n%2r>0]1)

,n=tk+r , r=n%kr==0:answer=t+1t>1:rkanswer=t+1r&1==1,k&1==0:answer=1r&1==0,k&1==1:answer=t+2t==1:r&1==0:answer=3r&1==1,k&1==0:answer=1;r&1==1,k&1==1:answer=2(n2r+[n%2r>0]1)

#include <stdio.h>int min(int a,int b){    return a>b?b:a;}int main (){    int n,k;    scanf("%d %d",&n,&k);    int t=n/k;    int r=n%k;    if(r==0)    {        printf("%d\n",t);        return 0;    }    if(t>1)    {        if((r&1)==(k&1))            printf("%d\n",t+1);        else        {            if(r&1)                printf("-1\n");            else                printf("%d\n",t+2);        }    }    else    {        if(r&1)        {            if(k&1)            {                if((r<<2)>=n)                    printf("4\n");                else                {                    int u=r<<1;                    int e=n/u;                    int ans;                    if(e>1)                        ans=(e+((n%u)?1:0))<<1;                    else                        ans=6;                    printf("%d\n",ans);                }            }            else                printf("-1\n");        }        else            printf("3\n");    }}