【YbtOj】折纸问题
题目链接:http://noip.ybtoj.com.cn/contest/17/problem/2
怎么说呢,这是一道看起来很复杂的水题,但不得不说,当程序出现问题是很难查出错误。
Saction A 输入/数据处理
嗯,这个部分看起来很智障,你只要知道什么是EOF,就行了。
现在我们来统一一下数据(统11下):
1 const int N = 100; //不用解释你也懂的 2 int pre[N]; //纸条初始状态 3 int nxt[N]; //纸条希望状态 4 int n, m; //应题目要求
Saction B 搜索
这个的搜索比较特殊,我们需要枚举分割线的情况,然后模拟纸条的翻折。我们同样从终止条件开始说起。
首先定义一下,我们规定当前序列长度(搜索深度)为nowlen,当前序列为一个数组a[],那么当前序列长度与目标序列长度相等时就应该终止这次的搜索,判断并回溯。由于此题的输出是能否达到目的而不是如何达到,所以这个搜索完全可以当做一个判断函数来使用。
上述判断可以用一个if来完成,在这个if中有以下几个步骤:第一,判断当前序列与目标序列是否吻合;第二,回溯。
第一个步骤用for循环就可以了,但是有一个细节需要注意,那就是这个纸条可以反过来。举个例子:我现在得到的纸条为:{1,2,3,4,5},但我的目标为{5,4,3,2,1},很明显,如果是现实,我们只用把我得到的纸条反过来就行了。所以在这个程序的判断中要写两个for循环,第一个for循环正着判断,第二个for循环反着判断。至于第二个步骤,return就行了(说这是一个步骤,总觉得有点智障)。代码如下:
1 if(nowlen==m) 2 { 3 bool flag = false; 4 bool can = false; 5 for (int i = 1; i <= m;i++) 6 { 7 if(a[i]!=nxt[i]) 8 { 9 flag = true; 10 break; 11 } 12 } 13 if(!flag) 14 return 1; 15 for (int i = 1; i <= m;i++) 16 { 17 if(a[i]!=nxt[m-i+1]) 18 { 19 can = true; 20 break; 21 } 22 } 23 if(!can) 24 return 1; 25 return 0; 26 }
接着就是dfs的核心。这个核心不是递归,而是模拟。
首先我们需要找到纸条的分割线,稍微有点dfs功底(我默认你能做这题就一定会写基本dfs),就知道开一个for循环,循环判断翻折的位置。接着对于每一个循环值也就是循环变量的值进行判断,其实,这个判断是一个可行性的剪枝。
如果翻折之后的长度比目标序列小那就肯定不是答案,直接放弃对它的搜索,假如它符合条件,接着就该模拟这个纸条的翻折了。
设定循环变量为i,那么在i作为分割线时,i+1与i绝对是对应的。设定一个临时数组temp,这个数组用作存储新的纸条。定义to=i+1,from=i,那么我们可以开一个while循环,每一次循环from--,to++。请想:如果from=0或者to=nowlen,那么这个重叠的部分肯定到头了,所以这两个条件就是while循环结束的条件。当然有可能这两个部分不完全重叠,此时就需要把不重叠的部分导入temp数组。因为这个纸条翻折之后,不是左边多出,就是右边多出。因为在第一个while循环里面from是递减的,想要知道是那边多出,就是看from的值。如果(!from),那么就应该是右侧多出,反之是左边多出,接着就只用把多余的数字导入temp就行了。
到此这个模拟就完成了,递归的时候,只用把新的temp的长度作为新的nowlen,temp作为新的a继续就可以了。
因为这是一个bool函数,所以最终只用把true或false返回就行了,结合下面代码,你会明白的:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 const int N = 100; 7 int pre[N]; 8 int nxt[N]; 9 int n, m; 10 bool dfs(int nowlen,int a[]) 11 { 12 if(nowlen==m) 13 { 14 bool flag = false; 15 bool can = false; 16 for (int i = 1; i <= m;i++) 17 { 18 if(a[i]!=nxt[i]) 19 { 20 flag = true; 21 break; 22 } 23 } 24 if(!flag) 25 return 1; 26 for (int i = 1; i <= m;i++) 27 { 28 if(a[i]!=nxt[m-i+1]) 29 { 30 can = true; 31 break; 32 } 33 } 34 if(!can) 35 return 1; 36 return 0; 37 } 38 int temp[16]; 39 bool ans = false; 40 for (int i = 1; i <= nowlen - 1;i++) 41 { 42 if(max(i,nowlen-i)<m) 43 continue; 44 int to = i + 1; 45 int from = i; 46 int cnt = 0; 47 while(from&&to<=nowlen) 48 { 49 temp[++cnt] = a[from] + a[to]; 50 from--; 51 to++; 52 } 53 if(!from) 54 { 55 while(to<=nowlen) 56 temp[++cnt] = a[to++]; 57 } 58 else 59 { 60 while(from) 61 temp[++cnt] =a[from--]; 62 } 63 ans = max(dfs(cnt, temp), ans);//寻找到一种可行方案即可 64 } 65 return ans; 66 } 67 int main() 68 { 69 while(scanf("%d", &n)!=EOF) 70 { 71 for (int i = 1; i <= n;i++) 72 scanf("%d", &pre[i]); 73 scanf("%d", &m); 74 for (int i = 1; i <= m;i++) 75 scanf("%d", &nxt[i]); 76 if(dfs(n,pre)) 77 puts("S"); 78 else 79 puts("N"); 80 } 81 return 0; 82 }
嗯,完美,抄代码的感觉的确很爽,不过运行程序时没有输出就是另外一个故事了!当然我上面的代码是绝对没有问题的!