HDU5965 扫雷 —— dp递推
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5965
题解:
1.
用a[]数组记录第二行的数字,用dp[]记录没一列放的地雷数。如果第一列的地雷数dp[1]已知,那么第二列的地雷数dp[2]可以确定了(因为a[1] = dp[0] + dp[1] + dp[2], dp[0]虚设), dp[2] = a[1] - dp[0] - dp[1]; 于是第三列也已知:dp[3] = a[2] - dp[1] - dp[2]。 所以状态转移方程为:dp[i] = a[i-1] - dp[i-2] - dp[i-1]。
所以只需枚举第一列的地雷数就可以了。
在递推的过程中,每一列的地雷数必须在0~2的范围内,如果超出,则方案不符合,break;
递推到第n+1个格子,如果dp[n+1] = 0, 则证明这种方案合法,更新答案。
2.
特殊点是i=0 和 i=n+1,因为可以假设他们存在,但却不能放地雷,所以可以通过他们判断方案是否合法。
突破口是i=1,因为i=1时,相邻边只有一条,知道自己,就可以知道相邻,然后一直递推下去。
代码如下:
#include<iostream>//hdu 5965 递推 #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #define mod 100000007 using namespace std; typedef long long LL; int dp[10005]; char a[10005]; int main() { int t,i,j,ans,sum; scanf("%d",&t); while(t--) { scanf("%s",a+1); int n = strlen(a+1); for(i = 1; i<=n; i++) a[i] -= '0'; ans = 0; for(i = 0; i<=a[1] && i<=2; i++)//枚举第一列,i为第一列的地雷数 { dp[1] = i; for(j = 2; j<=n+1; j++)//从第二列开始递推 { dp[j] = a[j-1]-dp[j-1]-dp[j-2]; if(dp[j]>2) break; } if(j==n+2 && dp[n+1] == 0) { sum = 1; for(j = 1; j<=n; j++) { if(dp[j]==1) sum = (sum*2)%mod; } ans = (ans + sum)%mod; } } printf("%d\n",ans); } }