【校内互测】拯救

245 篇文章 0 订阅
正义之士被恶魔抓了,被关在小黑屋里,无法继续他的正义事业,你决定去拯救他。 
关正义之士的小黑屋迅速被你打开,可是正义之士却被恶魔用一把锁给锁住了。这把锁包含了N个小锁。只有打开前K-2个锁,且锁上第K-1个锁,才能改变第K个锁的状态(打开或锁上该锁),第1个锁可以任意改变状态,当第1个锁锁上时第2个锁就可以改变状态。 

为了知道你到底是要留下来开锁,还是“走为上”,你需要知道到底需要多少次操作才能开锁(打开或锁上一把锁算一次操作,只有当N个小锁都被打开进才算开了锁)。

 输入save.in 

第一行为一个N(小锁的个数,1<=N<=1000)。 

第二行为n个整数a1,a2,...,an(每个都是0或者1),中间用单个空格隔开。如果是ai=1,表示第i个锁是锁着的,反之表示该锁已被打开。 

输出save.out 

包括一个数,表示最少要操作的次数。 样例输入 4 1010 样例输出 6 样例说明 

1010-->1110-->0110-->0100-->1100-->1000-->0000 对于40%的数据,有N<=30 对于100%的数据,有N<=1000; 

【题解】

用数学归纳法可知将一数列000...01(一共i个数,前i-1个数都是0,第i个数是1)经过法定规则变换成i个0所需要的步数是2^(i-1)-1;

设f[i]表示把前i个数都变成0所需要的步数,h[i]表示把前i个数变成000...01(i-1个0,最后一个是1)的最小步数;

那么

若a[i]=1:h[i]=f[i-1],f[i]=h[i-1]+2^(i-1)-1+1;

若a[i]=0:h[i]=h[i-1]+2^(i-1)-1+1,f[i]=f[i-1];

以上方程可以经过变换规则证明,目标为f[n]。

这道题为以后的dp提供了一个思路:当题目中明确给的转换规则可以在两个特殊的状态之间转换的话,dp状态就可以用这两种状态表示,并进行转换和递推。

事实上,这道题竟然与九连环和格雷码有关。

【代码&简单递推】

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,a[1005],f[1005],h[1005],mi[1005];
int main(){
	scanf("%d",&n);
	mi[0]=1; for (int i=1;i<=n;++i) mi[i]=mi[i-1]*2;
	for (int i=1;i<=n;++i)
	  scanf("%d",&a[i]);
	if (a[1]==0) f[1]=0,h[1]=1;
	else f[1]=1,h[1]=0;
	for (int i=1;i<=n;++i)
	  if (a[i]==1){
	  	h[i]=f[i-1];
	  	f[i]=h[i-1]+1+mi[i-1]-1;
	  }
	  else{
	  	h[i]=h[i-1]+1+mi[i-1]-1;
	  	f[i]=f[i-1];
	  }
	printf("%d\n",f[n]);
}

【代码&高精】

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,a[1005],f[1005][1005],h[1005][1005],mi[1005][1005],b[5];
inline void fuzhi(int a[],int b[]){
	for (int i=0;i<=b[0];++i)
	  a[i]=b[i];
}
inline void jiagao(int c[],int a[],int b[]){
	int len=max(a[0],b[0]);
	for (int i=1;i<=len;++i){
		a[i+1]+=(a[i]+b[i])/10;
		a[i]=(a[i]+b[i])%10;
	}
	if (a[len+1]) len++;
	a[0]=len;
	for (int i=0;i<=a[0];++i)
	  c[i]=a[i];
}
inline void chenggao(int c[],int a[],int b[]){
	int len=a[0]+b[0]-1;
	for (int i=1;i<=a[0];++i)
	  for (int j=1;j<=b[0];++j)
	  	c[i+j-1]+=a[i]*b[j];
	for (int i=1;i<=len;++i){
		c[i+1]+=c[i]/10;
		c[i]%=10;
	}
	while (c[len+1]){
		len++;
		c[len+1]+=c[len]/10;
		c[len]%=10;
	}
	c[0]=len;
}
int main(){
	freopen("save.in","r",stdin);
	freopen("save.out","w",stdout);
	scanf("%d",&n);
	b[0]=1; b[1]=2;
	mi[0][1]=mi[0][0]=1; for (int i=1;i<=n;++i) chenggao(mi[i],mi[i-1],b);
	for (int i=1;i<=n;++i)
	  scanf("%d",&a[i]);
	if (a[1]==0) f[1][1]=0,h[1][1]=1;
	else f[1][1]=1,h[1][1]=0;
	f[1][0]=h[1][0]=1;
	for (int i=1;i<=n;++i)
	  if (a[i]==1){
	  	fuzhi(h[i],f[i-1]);
	  	jiagao(f[i],h[i-1],mi[i-1]);
	  }
	  else{
	  	fuzhi(f[i],f[i-1]);
	  	jiagao(h[i],h[i-1],mi[i-1]);
	  }
	for (int i=f[n][0];i>0;--i)
	  printf("%d",f[n][i]);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值