【BZOJ 1088 扫雷Mine】模拟
http://www.lydsy.com/JudgeOnline/problem.php?id=1088
2*N的扫雷棋盘,第二列的值a[i]记录第 i 个格子和它8连通的格子里面雷的数目。
第一列的雷可能有多种方案满足第二列的数的限制,根据第二列的信息确定第一列雷有多少种摆放方案。
设第一列的值为b[i],不难得出以下递推关系
b[0] + b[1] = a[0]
b[0] + b[1] + b[2] = a[1]
...
b[i] = a[i-1] - a[i-2] + b[i-3]
或 b[i] = a[i-1] - b[i-1] - b[i-2]
由此发现,b[0]、b[1]确定了,后续的b[i]也由递推式唯一的确定了。而又有b[0]+b[1] = a[0]的限制条件,故可根据a[0]的值枚举b[0], b[1]的组合,然后顺次求出b[i], 判断每个b[i]值是否合法,一旦发现不合法,则此种枚举情况不成立。
程序流程图如下(这里的“合法”判断与具体位置有关,如a[0], a[n-1] <3, a[n-1] == b[n-1] + b[n-2]):
代码如下(为了直观表达流程图,用了goto语句):
1 #include <cstdio> 2 using namespace std; 3 4 int n; 5 int a[10005]; 6 int b[10005]; 7 8 int main() 9 { 10 scanf("%d", &n); 11 for(int i=0; i<n; i++){ 12 scanf("%d", &a[i]); 13 } 14 int cnt = 0; 15 16 for(int i=0; i<n; i++){ //非法 17 if(a[i]<0 || a[i]>3){ 18 goto L; 19 } 20 } 21 if(a[n-1]==3 || a[0] == 3){ //非法 22 goto L; 23 } 24 25 if(a[0] == 0) b[0] = b[1] = 0; 26 else if(a[0] == 2) b[0] = b[1] = 1; 27 28 if(a[0] == 1) goto B; 29 30 b[2] = a[1] - a[0]; //0或2的情况 31 if(b[2] < 0 || b[2] > 1){ 32 goto L; 33 } 34 for(int i=3; i<n; i++){ 35 b[i] = a[i-1] - a[i-2] + b[i-3]; 36 if(b[i] < 0 || b[i] > 1){ 37 goto L; 38 } 39 } 40 if(b[n-1] + b[n-2] != a[n-1]) //检查最后一个a 41 goto L; 42 printf("1\n"); 43 return 0; 44 45 B: b[0] = 0; b[1] = 1; //01或10的情况 46 b[2] = a[1] - a[0]; 47 if(b[2] < 0 || b[2] > 1){ 48 goto L1; 49 } 50 for(int i=3; i<n; i++){ 51 b[i] = a[i-1] - a[i-2] + b[i-3]; 52 if(b[i] < 0 || b[i] > 1){ 53 goto L1; 54 } 55 } 56 if(b[n-1] + b[n-2] != a[n-1]) 57 goto L1; 58 cnt++; 59 60 L1: b[0] = 1; b[1] = 0; //试探另一种 61 b[2] = a[1] - a[0]; 62 if(b[2] < 0 || b[2] > 1){ 63 goto L; 64 } 65 for(int i=3; i<n; i++){ 66 b[i] = a[i-1] - a[i-2] + b[i-3]; 67 if(b[i] < 0 || b[i] > 1){ 68 goto L; 69 } 70 } 71 if(b[n-1] + b[n-2] != a[n-1]) 72 goto L; 73 cnt++; 74 75 L: printf("%d\n", cnt); 76 return 0; 77 78 }