10I:核电站

总时间限制: 
5000ms
 
单个测试点时间限制: 
1000ms
 
内存限制: 
131072kB
描述

一个核电站有N个放核物质的坑,坑排列在一条直线上。如果连续M个坑中放入核物质,则会发生爆炸,于是,在某些坑中可能不放核物质。

任务:对于给定的N和M,求不发生爆炸的放置核物质的方案总数

输入

只一行,两个正整数N,M( 1 < N < 50,2 ≤ M ≤ 5 )

输出

一个正整数S,表示方案总数。

样例输入
4 3
样例输出
13
 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 int n, m;
 5 long long d[51][6];
 6 int main(){
 7     cin>>n>>m;
 8     //d(i,j)表示从1放到第i个坑,且最后i-j+1到i个坑连续放了j个炸弹的方案数
 9     //最后结果是d[n][0]+……+d[n][m-1]
10     d[1][0] = 1; d[1][1] = 1;
11     for(int i = 2; i <= n; i++){
12         for(int j = 0; j < m; j++){
13             if(j) d[i][j] = d[i-1][j-1];
14             else{
15                 for(int k = 0; k < m; k++)
16                     d[i][j] += d[i-1][k];
17             }
18         }
19     }
20     long long ans = 0;
21     for(int i = 0; i < m; i++)
22         ans+=d[n][i];
23     cout<<ans<<endl;
24      return 0;
25 }

备注:这道题还挺有意思的!这个思路也基本算是自己想出来了,包括的状态的定义和边界条件、最终解!但还是差了一点,没能坚持想下去,没能想到分j=0和j≠0两种情况来考虑!

我的思考过程是这样的:很容易想到,用d[i]来表示考虑前i个坑放置炸弹的方案数,但发现仅凭这个状态没有办法转移,因为还有不能连续放m个的要求。比如说考虑d[i+1]的时候,你也不知道前i个是咋放的,因此也就不能确定第i+1个坑是否能放炸弹。

然后我就想到,再加一个维度来记录,最后几个坑位连续放了j个炸弹这种情况,也就是说

d(i,j)表示从1放到第i个坑,且最后i-j+1到i个坑连续放了j个炸弹的方案数

这样我就可以知道第i个坑能不能放炸弹了!关键在于转移过程,我没能想到j分为=0和>0两种情况,关键还是对自己的思考不自信。实际上,如果j>0的话,那就要求是在d[i][j-1]的基础上放,而且第i个坑一定要放炸弹,这样才是连续放了j个,因此d[i][j]=d[i-1][j-1],那么对于=0的情况才是关键,=0的话就相当于第i个坑一定不放炸弹,那么前面就无所谓了,所以是d[i-1][k],k从0到m-1的加总。

注意答案和数组类型都要是long long!!


一维利用容斥原理也是可以解的!关键就在于i的分类讨论。思路如下:

 1 #include<cstdio>
 2 long long  dp[55];
 3 int main()
 4 {
 5     int n,m,i;
 6     scanf("%d%d",&n,&m);
 7     dp[0]=1;
 8     for (i=1;i<=n;i++)
 9         if (i==m) dp[i]=dp[i-1]*2-1;     //减掉都放。 
10         else
11         if (i<m) dp[i]=dp[i-1]*2;        //随便放不会炸。 
12         else dp[i]=dp[i-1]*2-dp[i-m-1];
13         /*后m个固定放时前面的有dp[i-m]种放法,这些放法中都不可取。
14         但是这些放法中包含了第(i-m)放与不放两种可能,第(i-m)放的可能在(i-1)中就已排除,所以只需再排除不放的情况,即dp[i-m-1]。*/ 
15     printf("%lld",dp[n]);
16     return 0;
17 }
原文地址:https://www.cnblogs.com/fangziyuan/p/13124524.html