luoguP2838 瓶子国的故事——倍增

luoguP2838

前言

记得这题是洛谷首页给我推荐的题。怪得很,我并没做过任何提交答案题,结果它给我推荐了一发,还是黑的。

好在有空余时间钻研,终于是把它搞出来了。

题解

这是一道xie题,让你写一个程序打印出另一个程序的源码,然后测试你程序打印出的程序能否完成任务。

完成什么任务呢?算 a + b a+b a+b ∣ a − b ∣ |a-b| ab gcd ⁡ \gcd gcd a ∗ b a*b ab之类,总之是一些对先进计算机语言来说很基础的运算。听起来挺简单,但是这题你只能用七种语句,除去输入输出只有五种有用的语句(题面说得很清楚我就不再列举了)。(当然如果你不想 AC 这道题,也可以用第八种语句)

而且这些语句有着令人头疼的弱点:只有顺序结构👍。

这意味着如果代码长了(要扣分),即使不会T,你也要用算法把他优化得短些,这就需要用到倍增等技巧。而且你需要想方设法实现选择结构

这些运算虽然基础,但是除了前四个子任务,后面的任务都需要 i f \rm if if 语句 判断。所以这题的难点就在于此。只要实现了高效的 i f \rm if if 语句,所有的问题都好解决了。

怎么实现 i f \rm if if 语句呢?回忆图灵机的原理,我们只需要实现根据某个瓶子水量是否为零来选择执行某个操作,就能实现其它各种各样的判断。我们可以叫它元判断(瞎JB乱扯的名字)。

我们知道,只要一个瓶子的水量为零,那么水量翻倍后还是为零。如果水量不为零,翻倍后会变成一个更大的不为零的数。所以我们还是用倍增,把瓶子里的水量不断翻倍。即使水量只有1,翻倍二十几次也能变成一个极大值。然后我们把翻倍后的水量copy成一个空瓶子 (题目中瓶子容积可以为零,谢天谢地),利用这个空瓶子来转运水,要么可以全部转运,要么一滴水也转运不了,也就是转运失败。这就实现了最基础的根据水量是否为零执行倒水操作😀。

然后就剩些细节了:

  • task 5:每次需要判断 a a a 2 i 2^i 2i 的大小关系,如果 a ≥ 2 i a≥2^i a2i 就要减去 2 i 2^i 2i ,可以搞一个水量 a + 1 a+1 a+1 的瓶子,再向容量为 2 i 2^i 2i 的空瓶倒水,判断剩余水量是否大于0。同理,可以把剩余水倒入一个大小为1的瓶子(相当于整型转布尔型),这个瓶子的水量就是这个二进制位上的值。
  • task 6:倍增乘法。每次判断 a a a 减去2的某次幂后是否 ≥ 0 ≥0 0,所以还是得先加1转化为判断大于0。
  • task 7:异或就是不进位的加法。好好运用来之不易的 i f \rm if if 语句,来实现模运算吧。
  • task 8:整除操作和取模操作非常相似,只需要在倍增减去除数的同时记录减了多少即可。
  • task 9:乘法中取模,注意此处取模不要倍增,只减一次就行。
  • task 10:注意在倍增乘法执行失败时乘的是1不是0。
  • 全局大坑:新建或copy后的瓶子是空的,而copy的又是水量不是容积,所以记得不要少了Fill操作。

代码

对不起,这是道提答题。我对每个子任务单独写了不超过200行的生成代码的代码,所以码量太大搬不上来。

但是,我是有码人士,所以我会给一个我写的乘法操作以供参考。

inline int mul(int a,int b,int ti){//ti是倍增次数,调节用的
	printf("C 233333333\nC 233333333\n"),d+=2;
	int p=d-1,q=d;
	printf("C 1\nM %d\n",b),d+=2;
	f[0]=d-1,g[0]=d;
	for(int i=1;i<=ti;i++){
		printf("C %d\n",1<<i),d++;
		f[i]=d;
		printf("F %d\nT %d %d\n",g[i-1],g[i-1],p);
		printf("F %d\nT %d %d\n",g[i-1],g[i-1],p);
		printf("M %d\n",p),d++;
		g[i]=d;
		printf("E %d\n",p);
	}
	printf("F %d\nT %d %d\nT %d %d\nE %d\n",f[0],f[0],p,a,p,f[0]);
	printf("M %d\nE %d\nF %d\n",p,p,d+1),d++,a=d;
	for(int i=ti;i>=0;i--){
		printf("M %d\nF %d\n",a,d+1),d++;
		printf("T %d %d\n",d,f[i]);
		printf("M %d\n",d),d++;
		int c=d;
		printf("F %d\nT %d %d\n",c,c,p);
		for(int j=1;j<=21;j++){//倍增if
			printf("M %d\nF %d\n",p,d+1),d++;
			printf("T %d %d\n",d,p);
		}
		printf("M %d\nE %d\n",p,p),d++,c=d;
		printf("F %d\nT %d %d\n",g[i],g[i],c);
		printf("T %d %d\n",c,q);
		printf("F %d\nT %d %d\n",f[i],f[i],c);
		printf("M %d\n",c),d++;
		printf("T %d %d\n",a,d);
	}
	return q;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值