【noi 2.6_9267】核电站(DP)

题意:n个数中不能同时选连续m个或以上,问方案数。

解法:f[i][j]表示从前i个中选,到第i个已经连续选了j个。
j!=0时,  =f[i-1][j-1] ; j=0时, =f[i-1][0~m-1] ;

优化1:f[i][m]存f[i-1][0~m-1],就不用多for一重。

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 using namespace std;
 5 
 6 long long f[55][10];
 7 int main()
 8 {
 9     int n,m;
10     scanf("%d%d",&n,&m);
11     f[1][0]=f[1][1]=1,f[1][m]=2;
12     for (int j=2;j<m;j++) f[1][j]=0;
13     for (int i=2;i<=n;i++)
14     {
15      long long h=0;
16      f[i][0]=f[i-1][m];
17      f[i][1]=f[i-1][0];
18      h=f[i][0]+f[i][1];
19      for (int j=2;j<m;j++)
20      {
21        f[i][j]=f[i-1][j-1];
22        h+=f[i][j];
23      }
24      f[i][m]=h;
25     }
26     printf("%lld\n",f[n][m]);
27     return 0;
28 }
View Code 1

优化2:(由上面的方程推出)f[i]表示从前i个数中合法的方案数,即之前的f[i][m]。

i<m时,f[i]=2*f[i-1];每次对于第i个数可选或不选,方案数都各为f[i-1]。
i>=m时,则再-f[i-m-1];由于i=m时,i-m-1<0则单独写出了来-1(1~m的数都选的状态)。
可知-f[i-m-1]是因为每次f[i-1][]中不合法的都是f[i-1][m-1],而它也是从最初的  f[i-1][m-1]=f[i-2][m-2]=  ...  =f[i-m][0]=f[i-m-1][0~m-1]=f[i-m-1][m]连等上来的,也就是说要减去已经连续选了m-1个的方案数:f[i-m-1]。

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 using namespace std;
 5 
 6 long long f[55];
 7 int main()
 8 {
 9     int n,m;
10     scanf("%d%d",&n,&m);
11     f[0]=1;
12     for (int i=1;i<=m;i++) f[i]=2*f[i-1];
13     f[m]--;
14     for (int i=m+1;i<=n;i++) f[i]=2*f[i-1]-f[i-m-1];
15     printf("%lld\n",f[n]);
16     return 0;
17 }
View Code 2

注意——f[n][m]不是答案最大的状态,而是f[n][1]。因此看是否要用long long得看f[n][1]......

posted @ 2016-10-14 20:21  konjac蒟蒻  阅读(412)  评论(0编辑  收藏  举报