51nod 1613 翻硬币
有 n 个硬币,一开始全部正面朝上,
每次可以翻转 k 个硬币( k 小 于 n ),
那么至少要 p 次翻转,才能让所有硬币反面朝上,
求 p 的值。如果不能成功翻转则输出-1
这个题目 重点是对已有思路的想法的梳理
题目中 是要求 每次都要反转k个硬币。
我们每次反转 k个硬币,并通过一部分重叠 。
来凑出恰好k个硬币正面朝上。
也就是这一次反转的k个硬币可能会有上一次已经反转过的硬币。
一个硬币最多被反转两次,
那么每重复反转了一个硬币,就会比 不重复反转的时候多出来两个
没有反转的硬币。
例如 5个硬币 每次反转2个
开始的时候
0,0,0,0,0
连续反转两次,两次没有重叠
1,1,1,1,0
连续反转两次,有一个重叠
1,0,1,0,0
有一个重叠的时候 ,因为出现了一次重叠,那么被反转的区间减小1,
同时,重叠部分反转回去,所以反面朝上的硬币数量比上面小了2
这个减小的数量 正好是重叠数量的2倍关系,也为我们凑出合适的区间,
埋下伏笔。
令
n=tk+r,其中r=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次
因为 此时我们多反转了
k−r
次 又因为
k−r
是一个奇数
而被翻动超过一次的硬币必须增加偶数次反转
所以
answer!=t+1
所以
answer>t+1
证明 对于
k+r
个硬币,
k
为奇数,r 为偶数,(如上文前提)
只需要 3次操作
当我们连续两次反转的时候,第二次反转k个硬币中,有A个是上一次反转的
那么这个时候,真正被反转的,只有:
2(k−A)
这是一个偶数,同时有
0<=2(k−A)<=2r
所以我们可以使用这种连续两次反转的方法 来吧其中r个硬币反转
同时 剩余的k个硬币只需要一次反转
总计 3次
那么对于
n=tk+r
反转次数为
t−1+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(k−A)<=2r
个硬币的反转,也就是说我们用
2(k−A)
来替代
k
,这样问题就可以规约到 r为偶数 k为偶数的情况
现在证明,这种方法得到的
依然有前提 k与r均为奇数
因为 n为 偶数 所以有
pk=∑i=1nqi 为偶数
所以,p为偶数,那么问题等价于每次的操作都是连续两次的反转。
所以用任意恰当的数字
2a(2a<=2r)
来替代k得到的 答案
answer=2(⌊n2r⌋+[n%2r>0]∗1)
综上,n=tk+r , r=n%k∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗r==0:answer=t+1∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗t>1:r于k的奇偶性相同时:answer=t+1r&1==1,k&1==0:answer=−1r&1==0,k&1==1:answer=t+2∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗t==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");
}
}