关正义之士的小黑屋迅速被你打开,可是正义之士却被恶魔用一把锁给锁住了。这把锁包含了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]);
}