【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 }

 

嗯,完美,抄代码的感觉的确很爽,不过运行程序时没有输出就是另外一个故事了!当然我上面的代码是绝对没有问题的!

posted on 2021-08-07 17:26  百里狂生  阅读(122)  评论(0编辑  收藏  举报

导航