其实暴力搜索很好想到,但是你就算剪枝剪到极限,也拿不了满分,因为这个题暴搜的时间复杂度是o(2n),肯定超时了,但是你剪枝剪的好一点就可以拿60的分,但是dfs好想一点在考场上优先选择吧,所以我们先看看dfs是怎么解题的,说来也惭愧这题dp刚开始一直想不对,大概模型是对的,但是有点小细节没处理好,导致结果一直出不来,所以借鉴了下别人的想法,我们先看下这个题给的样例参考代码
首先的话就是这个题说的遇到花,酒数量减一,遇到店酒的数量翻倍,那么好突破口就再这里,你只需要每次dfs的时候分别讲这两种情况都进行判断就好了,下面我们看一下剪枝怎么剪.
if(x>N||y>M) //如果超出去就剪枝 { return; }
if(x<N&&y==M) //花遇见完了,但是还有店没有遇见完全就剪枝 { return; } if(x==N&&M-y!=k) //店遇见完了,但是后面剩的花不足以将酒喝完就剪枝 { return; }
if(k==-1&&arr[i-1]==0) //判断没有酒了但是还是遇见花,这是不合理的 { return; }
if(x==N&&y==M&&k==0&&arr[i-1]!=1) //这里是判断最后一次不能遇到店,只能遇到花// { flag=(flag+1)%mod; return; }
#include<stdio.h> #include<string.h> #define mod 1000000007 int N,M,flag=0,k=2,i=1,arr[105]; void dfs(int x,int y) { if(x>N||y>M) //如果超出去就剪枝 { return; } if(x<N&&y==M) //花遇见完了,但是还有店没有遇见完全就剪枝 { return; } if(x==N&&M-y!=k) //店遇见完了,但是后面剩的花不足以将酒喝完就剪枝 { return; } if(k==-1&&arr[i-1]==0) //判断没有酒了但是还是遇见花,这是不合理的 { return; } if(x==N&&y==M&&k==0&&arr[i-1]!=1) //这里是判断最后一次不能遇到店,只能遇到花// { flag=(flag+1)%mod; return; } arr[i]=0; //遇到花了 k--; i++; dfs(x,y+1); i--; k++; arr[i]=1; //遇到店了 k*=2; i++; dfs(x+1,y); i--; k/=2; } int main() { memset(arr,0,sizeof(arr)); scanf("%d%d",&N,&M); dfs(0,0); printf("%d",flag%mod); return 0; }
我们可以讲这个题看成是一个二维的矩阵,来走方格;
用题目中给的例子,5个店,10个花
向下走我们此时手里面的酒就会翻倍,向右走我们手中的酒就会减一
所以刚开始我们手中的酒就是2然后我们必须保证走到最后一格格子剩下的酒必须是0,而且我们最后一步必须遇到的是花,所以如果只是开二维的话会存在后效性,我们必须开到三维才可以,所以第三维我们记录现在所含有的酒就好了
2 | 1 | 0 | ...... | ......... | ...... | ....... | ...... | ..... | ........ |
4 | 3/2 | 0/2/1 | 1/0 | 0 | ........ | ........ | ....... | ...... | ...... |
8(@) | 7/6/4 | 0/4/2/6/5/4 | |||||||
16 | 14/12/8 | ||||||||
.... |
@:从8那个点开始后面的就不需要算了,因为假如你再遇到店,那么你此时手里面的酒就是16个,你总共的花才10个所以就不可能喝完了,所以我们就直接不用算下面的了
.......:表示不用算了
但是这样算有个不能保证最后一步必须是遇到花了,所以我们从终点往起点走就好了.
#include<stdio.h> #include<string.h> #define mod 1000000007 #define max 105 int dp[max][max][max]; int main() { int N,M,flag=0,i,j,k; scanf("%d%d",&N,&M); memset(dp,0,sizeof(dp)); dp[N][M][2]=1; //初始化刚开始手里面是有2斗酒 for(i=N; i>=0; i--) { for (j = M; j>=0; j--) { for (k = M; k>=0; k--) { if(2*k<=M&&i>=1) { dp[i-1][j][k<<1]=(dp[i][j][k]+dp[i-1][j][k<<1])%mod; } if(j>=1&&k>=1) { dp[i][j-1][k-1]=(dp[i][j-1][k-1]+dp[i][j][k])%mod; } } } } printf("%d",dp[0][1][1]%mod); return 0; }
时间复杂度o(NM2)
0.0分
54 人评分
其实dp那里没有讲仔细吧。应该是 dp[i-1][j][k<<1]=dp[i][j][k]; dp[i][j-1][k-1]+=dp[i][j][k]; 第一次也就是 dp[i-1][j][k<<1]=dp[i][j][k]这一步其实把加号去掉更容易理解,就是说当剩余酒量k为偶数时,那么必然有当前所遇为店(因为遇店酒量就乘以二为偶数)这是显然的也是必然的,之所以说它是必然的,在于紧跟着的dp[i][j-1][k-1]+=dp[i][j][k],这一步是对上一步dp[i-1][j][k<<1]=dp[i][j][k]的补充,也就是说如果酒量k为偶数不一定就是遇到了店,也有可能是遇到了花(比如之前的酒量k为奇数)所以有了这一步的补充才使得第一步dp[i-1][j][k<<1]=dp[i][j][k]连同他的条件成立(条件是酒量k为偶数)。
A+B for Input-Output Practice (VII) (C语言代码)浏览:1415 |
C语言程序设计教程(第三版)课后习题9.3 (C++代码)浏览:702 |
【偶数求和】 (C语言代码)记得sum的归零时机浏览:989 |
高精度加法 (C++代码)(大数加法)浏览:1008 |
C二级辅导-统计字符 (C语言代码)浏览:529 |
C语言程序设计教程(第三版)课后习题5.7 (C语言代码)浏览:783 |
Pascal三角 (C语言代码)浏览:1252 |
WU-复数求和 (C++代码)浏览:2119 |
【矩阵】 (C++代码)浏览:999 |
【计算球体积】 (C语言代码)浏览:1158 |