其实暴力搜索很好想到,但是你就算剪枝剪到极限,也拿不了满分,因为这个题暴搜的时间复杂度是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为偶数)。
2003年秋浙江省计算机等级考试二级C 编程题(2) (C语言代码)浏览:534 |
C语言程序设计教程(第三版)课后习题4.9 (C语言代码)浏览:377 |
C语言程序设计教程(第三版)课后习题6.1 (C语言代码)浏览:695 |
Cylinder (C语言描述+详细分析)浏览:3258 |
C语言程序设计教程(第三版)课后习题9.3 (C语言代码)浏览:668 |
循环入门练习5 (C语言代码)浏览:828 |
C语言程序设计教程(第三版)课后习题10.7 (用指针求解)浏览:1457 |
简单的a+b (C语言代码)浏览:531 |
分解质因数 (C++代码)浏览:1470 |
C语言程序设计教程(第三版)课后习题5.4 (C语言代码)浏览:454 |