NOIP2012普及组 T3 摆花(加强版)

【题目大意】
用 n 种花摆共 m 盆花,每盆仅能摆一种花,不分每种花、每盆花的顺序,第 i 种花可以不摆,最多摆 ai 盆,求方案数。
【输入格式】
共 2 行。第一行包含两个正整数 n 和 m,中间用一个空格隔开。第二行有 n 个整数,每两个整数之间用一个空格隔开,依次表示 a1 a2 、…… an
【输出格式】
一个整数,方案数对1000007取模的结果。
【数据范围】
对于10%的数据, 0<n,m500ai50
对于30%的数据, 0<n,m1000ai100
对于60%的数据, 0<n,m23330ai2333
对于100%的数据, 0<n,m88480ai8848

〇、网上说法的一些问题

加了一些特别大的数据是因为网上大部分做法都是 O(nm2) ,没法对付n, m上千的情况。本题有时间 O(nm) ,空间 O(n+2m) 的做法。
网上说 O(nm2) 是背包的,是因为

对于一个给定了背包容量、物品费用、物品间相互关系(分组、依赖等)的背包问题,除了再给定每个物品的价值后求可得到的最大价值外,还可以得到装满背包或将背包装至某一指定容量的方案总数
对于这类改变问法的问题,一般只需将状态转移方程中的 max 改成 sum 即可。——《背包九讲》

不过,严格来说这不能算是背包,叫递推可能好点儿,因为动态规划专指解决最优化问题(求最大\小\快\慢)。当然分析时以及递推式与背包很像。

一、10%,时间 O(n2m2) ,空间 O(nm)

最初想的时候把状态搞复杂了……用 fi,j 表示用前 i 种花摆前 j 盆时用第 i 种花摆第 j 盆,则可得递推式

fi,j=k=0i1l=jaij1fk,l

也就是用前0~i-1种花摆j- ai 盆时用第i-1种花摆第j- ai 盆(则第 i 种花摆 ai 盆)的方案数,加上用前0~i-1种花摆j- ai +1盆时用第i-1种花摆第j- ai +1盆(第 i 种花摆 ai -1盆)的方案数,一直到用前0~i-1种花摆j-1盆时用第i-1种花摆第j-1盆的方案数。
另外,在上面的递推式中,如果j< ai ,递推式变为
fi,j=k=0i1l=0j1fk,l

边界条件, f0,0 =1,结果是 fi,j=ni=1fi,m

  f[0][0] = 1;
  for (i = 1; i <= n; i++)
    for (j = 1; j <= m; j++)
      for (k = 0; k < i; k++)
        for (l = max(0, j - a[i]); l < j; l++)
          (f[i][j] += f[k][l]) %= mod;
  for (i = 1; i <= n; i++)
    (ans += f[i][m]) %= mod;

虽说这方法很low但是我最早是通过这个方法推出O(nm)的方法的嗯。

二、30%,时间 O(nm2) ,空间 O(nm)

如果 fi,j 只是表示用前 i 种花摆前 j 盆(不一定要用第 i 种花),递推式便缩减成

fi,j=k=jaij1fi1,k

也就是(当第 i 种花摆 ai 盆时)用前i-1种花摆j- ai 盆的方案数,加上(当第 i 种花摆 ai -1盆时)用前i-1种花摆j- ai +1盆的方案数,一直到(当第 i 种花只摆1盆时)用前i-1种花摆j-1盆的方案数。
边界变为 fi,0 =1,结果是 fi,j

for (i = 0; i <= n; i++)
  f[i][0] = 1;
for (i = 1; i <= n; i++)
  for (j = 1; j <= m; j++)
    for (k = max(0, j-a[i]); k < j; k++)
      (f[i][j] += f[i - 1][k]) %= mod;
ans = f[n][m];

三、60%,时间 O(nm) ,空间 O(nm)

推出这种方法的思路,一种是 O(n2m2) 的方法加二维前缀和。
在一维情况下,例如求 f3 f8 之和。用 sumx 表示 xi=0fi ,很明显 f3 f8 之和= sum8sum2

f0123456789

推广到二维情况,用 sumx,y 表示 xi=0yj=0fi,j ,如求 f1,2 f4,5 的和(蓝色区域)。
这里写图片描述
很明显,蓝色部分的和=图中整个区域-橙色区域-绿色区域+屎黄色区域= sum4,5sum4,1sum0,5+sum0,1

不过这题的前缀和倒简单一点。当j< ai 时,

fi,j=k=0i1l=0j1fk,l=sumi1,j1

而当j≥ ai
fi,j=k=0i1l=jaij1fk,l
注意到k是从0开始的,因此只要把 l 这层拆开。
fi,j=k=0i1l=0j1fk,lk=0i1l=0jai1fk,l=sumi1,j1sumi1,jai1

这里写图片描述
也就是 fi,j 等于整个蓝色区域减去浅蓝色区域(不要在意为什么图片的字体不同)。
还有一点是更新sum数组。
这里写图片描述
如图, sum6,5=sum4,6+sum5,5sum4,5+f6,5 sum6,5 等于 f6,5 f6,5 的左方和上方的所有数之和。用前缀和的思想, f6,5 的左方和上方的所有数一部分是 sum4,6 ,另一部分是 sum5,5 ,而重叠的部分是 sum4,5
所以
sumi,j=sumi1,j+sumi,j1sumi1,j1+fi,j

当j< ai 时,
sumi,j=sumi1,j+sumi,j1sumi1,j1+sumi1,j1=sumi1,j+sumi,j1

当j≥ ai 时,
sumi,j=sumi1,j+sumi,j1sumi1,j1+sumi1,j1sumi1,jai1=sumi1,j+sumi,j1sumi1,jai1

这样就可以吃掉 f 数组了。
由于是从 O(n2m2) 的方法推出来,所以 sum0,0=f0,0=1 ,由于是前缀和,同理要把 sumi,0 sum0,i 都清成1。

sum[0][0] = 1;
  for (i = 1; i <= n; i++)
    sum[i][0] = 1;
  for (i = 1; i <= m; i++)
    sum[0][i] = 1;
  for (i = 1; i <= n; i++)
    for (j = 1; j <= m; j++)
      if (a[i] <= j)
        sum[i][j] = (sum[i - 1][j] + sum[i][j - 1]) % mod;
      else
        sum[i][j] = (sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - a[i] - 1] + mod) % mod;
  ans = (sum[n][m] - sum[n][m - 1] + mod) % mod;

绕了一大圈,我倒发现这个递推式本不用这么复杂的推导。用 sumi,j 表示用前 i 种花摆前0~j盆花(不摆花+只摆第1盆花+摆前2盆花+…+摆前j盆花)的方案数。不用第 i 种花摆第 j 盆的方案数为 sumi1,j 。用第 i 种花摆第 j 盆的方案数为 sumi,j1 ,但当j≥ ai 时,还要减去用前 i-1 种花摆前0~j- ai -1盆花的方案数(因为前提是用第 i 种花摆第 j 盆,当用前 i-1 种花摆前 j- ai -1盆花时,就要摆第 i 种花 ai +1盆;当用前 i-1 种花摆前 j- ai -2盆花时,就要摆第 i 种花 ai +2盆,以此类推)。

四、100%,时间 O(nm) ,空间 O(n+2m)

呼我为什么要把那种复杂的分析方法写上……
在这个数据范围下时间复杂度是可以承受的,但空间炸了。可以注意到无论是 sumi,j=sumi1,j+sumi,j1 还是 sumi,j=sumi1,j+sumi,j1sumi1,jai1 的 i 那层只有 i 和 i-1两种情况,所以用滚动数组可以解决空间问题。

sum[0][0] = sum[1][0] = 1;
  for (i = 1; i <= m; i++)
    sum[0][i] = 1;
bool t;
  for (i = 1, t = 1; i <= n; i++, t = !t)
    for (j = 1; j <= m; j++)
      if (j <= a[i])
        sum[t][j] = (sum[t][j - 1] + sum[!t][j]) % mod;
      else
        sum[t][j] = (sum[t][j - 1] + sum[!t][j] - sum[!t][j - a[i] - 1] + mod) % mod;

写完后我联系到01背包的空间优化方法本以为有一维数组的做法,但思虑再三发现不行。
这里写图片描述
在01背包的空间优化中,求灰色格的值需要用到的两个值都在灰色格的左边和上边。
这里写图片描述
但是在本题中,根据递推式,上图中&%#¥¥#@%……&¥总之就是不行啦求大佬看一下怎么说清楚
感谢陈老师~

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值