洛谷P1371 NOI元丹

来源:互联网 发布:国家护理质量数据 编辑:程序博客网 时间:2024/04/28 23:28

查找方案数
动态规划的转移方程挺容易想的
暴力把N转成1,O转成2,I转成3
s[i]表示原字符串转成的数字串
f[i][j]表示用前 i 个字符拼 ‘NOI’ 中的 j 对应的字符的方案数
那么对于s[i],可以用它来拼 j ,也可以不用它来拼 j 而采用前i-1中已拼好的方案
则,状态转移方程为:
f[i][j]=f[i-1][j]+f[i-1][j-1] (s[i]=j) //此处[j-1]只是指j只能由j-1拼成
f[i][j]=f[i-1][j] (s[i]!=j)

观察状态转移方程,可以发现f数组只用到 i 和i-1
所以可以把f[i][j]优化为f[j]
则,状态转移方程为:
f[j]=f[j]+f[j-1]
当我们要对f[j]进行转移时,f[j]中实际存的是f[i-1][j],故方程可行
需注意 j 的枚举顺序

在把 f 数组优化至一维后,再次观察原方程,我们发现:
在转移状态之前,实际上我们就已经有了:
f[i][j]=f[i-1][j]
那么我们就可以不枚举 j ,直接转移:f[s[i]]+=f[s[i]-1]

代码:
注意一下初始化,f[0][0]=1表示有不用任何字符的方案

原始转移方程

inline void dp(){  memset(f,0,sizeof(f));  f[0][0]=1;  for(int i=1;i<=n;i++)    for(int j=1;j<4;j++){      f[i][j]=f[i-1][j];      if(j==s[i])        f[i][j]+=f[i-1][j-1];    }  if(f[3]>ans)ans=f[3];}

优化:

inline void dp(){  f[0]=1;  for(int j=1;j<4;j++)f[j]=0;  for(int i=1;i<=n;i++)    f[s[i]]+=f[s[i]-1];  if(f[3]>ans)ans=f[3];}

程序:
dp函数略有改动

#include <cstdio>inline int re(){  int x; char ch;  while((ch=getchar())>'9'||ch<'0');  x=ch-48;  while((ch=getchar())>='0'&&ch<='9')x=(x<<1)+(x<<3)+ch-48;  return x;}const int maxn=200005;int n;char str;int s[maxn];long long ans;long long f[4];inline void dp(int l,int r){  f[0]=1;  for(int i=1;i<4;i++)f[i]=0;  for(int i=l;i<=r;i++)    f[s[i]]+=f[s[i]-1];  if(f[3]>ans)ans=f[3];}int main(){  n=re();  scanf("\n");  for(int i=1;i<=n;i++){    scanf("%c",&str);    if(str=='N')s[i]=1;    else if(str=='O')s[i]=2;    else s[i]=3;  }  s[0]=1;  dp(0,n);  s[n+1]=3;  dp(1,n+1);  //数据太水了,没讨论'O'  /*  long long tmp=0;  int cnt[2]={0};  for(int i=1,flag=0;i<=n;i++){    if(flag&&(s[i]==1||i==n)){      tmp=(long long)cnt[0]*cnt[1];      ans=ans>tmp?ans:tmp;      if(i==n)break;      flag=0;      cnt[0]=cnt[1]=0;    }    else if(s[i]==3)flag=1;    cnt[flag]++;  }  */  printf("%lld",ans);  return 0;}
0 0
原创粉丝点击