P1805 关灯 题解


P1805 关灯 题解

很明显的递推题,但是高精度。

解法

相信大家都对九连环的游戏有所了解。如果不拆除前面的环,那后面的环也就不能拆除。有依赖关系。

考虑设置状态。

题中要求所有灯全部熄灭所用的步数,所以这里设置状态 f f f f i f_i fi 表示前 i i i 盏灯全部熄灭所用的最少步数。由于第 i i i 盏灯可以被操作的前提是前 i − 1 i-1 i1 盏灯满足一定条件,所以只能从左到右顺序操作。现在考虑如何转移。

如果第 i i i 盏灯本来就是灭的。那这个位置不用操作, f i = f i − 1 f_i = f_{i-1} fi=fi1

如果第 i i i 盏灯本来是亮的,那这个位置需要操作。此时已经处理了前 i − 1 i-1 i1 盏灯的情况。这里分两种情况讨论。

为了辅助这个过程,我们再设一个状态 g g g g i g_i gi 表示在前 i i i 盏灯全部熄灭的情况下点亮第 i i i 盏灯所用的最少步数,这里等价于前 i − 1 i-1 i1 盏灯全部熄灭、第 i i i 盏灯点亮的情况下使前 i i i 盏灯全部熄灭所用的最少步数。题中说编号为 1 1 1 的灯可以随意开或关,所以 g 1 = 1 g_1 = 1 g1=1。考虑 g 2 g_2 g2 g 2 g_2 g2 即为开 1 1 1,开 2 2 2,关 1 1 1 的最少步数,即为 g 1 × 2 + 1 g_1 \times 2 + 1 g1×2+1。考虑 g i g_i gi,即为开 i − 1 i-1 i1,开 i i i,关 i − 1 i-1 i1 的最少步数,即为 g i − 1 × 2 + 1 g_i-1 \times 2 + 1 gi1×2+1。利用数学归纳法:

g 1 = 1 = 2 1 − 1 g 2 = g 1 × 2 + 1 = ( 2 1 − 1 ) × 2 + 1 = 2 1 + 1 = 2 2 − 1 g 3 = g 2 × 2 + 1 = ( 2 2 − 1 ) × 2 + 1 = 2 3 − 2 + 1 = 2 3 − 1 g i = g i − 1 × 2 + 1 = ( 2 i − 1 − 1 ) × 2 + 1 = 2 i − 2 + 1 = 2 i − 1 \begin{aligned} &g_1 = 1 = 2^1 - 1 \\ &g_2 = g_1 \times 2 + 1 = (2^1 - 1) \times 2 + 1 = 2^1 + 1 = 2^2 -1 \\ &g_3 = g_2 \times 2 + 1 = (2^2 -1) \times 2 +1 = 2^3 - 2 + 1 = 2^3 - 1 \\ &g_i = g_{i-1} \times 2 + 1 = (2^{i-1} - 1) \times 2 + 1 = 2^i - 2 + 1 =2^i - 1\end{aligned} g1=1=211g2=g1×2+1=(211)×2+1=21+1=221g3=g2×2+1=(221)×2+1=232+1=231gi=gi1×2+1=(2i11)×2+1=2i2+1=2i1

得出结论: g i = 2 i − 1 g_i = 2^i - 1 gi=2i1

  • 如果第 i − 1 i-1 i1 盏灯本来是亮的。则 f i = g i − 1 − f i − 1 + 1 + g i − 1 = g i − f i − 1 f_i = g_{i-1} - f_{i-1} + 1 + g_{i-1} = g_i - f_{i-1} fi=gi1fi1+1+gi1=gifi1
  • 如果第 i − 1 i-1 i1 盏灯本来是熄灭的的。则 f i = g i − 1 − f i − 2 + 1 + g i − 1 = g i − 1 − f i − 2 + 1 + g i − 1 = g i − f i − 2 = g i − f i − 1 f_i = g_{i-1} - f_{i-2} + 1 + g_{i-1} = g_{i-1} - f_{i-2} + 1 + g{i-1} = g_i - f_{i-2} = g_{i} - f_{i-1} fi=gi1fi2+1+gi1=gi1fi2+1+gi1=gifi2=gifi1

综上, f i = { f i − 1 a i = 0 g i − f i − 1 a i = 1 f_i = \begin{cases}f_{i-1}&a_i = 0\\g_i - f_{i-1}&a_i = 1\end{cases} fi={fi1gifi1ai=0ai=1

高精度略。

代码

#include<bits/stdc++.h>
template <typename T>
inline void swap(T &x,T &y)
{
	T tmp=x;
	x=y,y=tmp;
}
class high_accuracy
{
private:
	int len,a[5000];
public:
	inline int &operator[](const int &x)
	{
		return a[x];
	}
	inline int size()
	{
		return len;
	}
	inline high_accuracy()
	{
		len=0,a[0]=a[1]=a[2]=0;
	}
	inline void init(__int128 x)
	{
		len=0;
		if(x==0) len=1,a[1]=0;
		while(x) a[++len]=x%10,x/=10;
	}
	inline void deal(int l)
	{
		for(int i=1;i<=l;i++)
		{
			while(a[i]<0) a[i+1]--,a[i]+=10;
			a[i+1]+=a[i]/10,a[i]%=10;
		}
		len=l;
		while(!a[len]) len--;
	}
	inline void print()
	{
		for(int i=std::max(len,1);i;i--) std::cout<<a[i];
		std::cout<<"\n";
	}
	inline high_accuracy operator+(high_accuracy rhs)
	{
		high_accuracy ret;
		int le=std::max(len,rhs.size());
		for(int i=1;i<=le+3;i++) ret[i]=0;
		for(int i=1;i<=le;i++) ret[i]+=a[i]+rhs[i];
		ret.deal(le+2);
		return ret;
	}
	inline high_accuracy operator-(high_accuracy rhs) // 适用于 *this 比 rhs 大的情况
	{
		high_accuracy ret;
		int le=std::max(len,rhs.size());
		for(int i=1;i<=le+3;i++) ret[i]=0;
		for(int i=1;i<=le;i++) ret[i]+=a[i]-rhs[i];
		ret.deal(le+2);
		return ret;
	}
	inline high_accuracy operator*(high_accuracy rhs)
	{
		high_accuracy ret;
		for(int i=1;i<=len+rhs.size()+5;i++) ret[i]=0;
		for(int i=1;i<=len;i++) for(int j=1;j<=rhs.size();j++) ret[i+j-1]+=a[i]*rhs[j];
		ret.deal(len+rhs.size()+5);
		return ret;
	}
};
high_accuracy f[1010],pw[1010];
int n,a[1010];
int main()
{
	std::cin>>n,pw[0].init(1),pw[1].init(2),f[0].init(0);
	for(int i=1;i<=n;i++) std::cin>>a[i];
	for(int i=2;i<=n;i++) pw[i]=pw[i-1]*pw[1];
	if(a[1]) f[1].init(1);
	for(int i=2;i<=n;i++)
		if(a[i]) f[i]=pw[i]-pw[0]-f[i-1];
		else f[i]=f[i-1];
	f[n].print();
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值