【YbtOJ】拔河比赛
题目链接:http://noip.ybtoj.com.cn/contest/16/problem/1
比较简单的一题,既然要深度优先搜索,那么就要考虑清楚几点:
- 递归或者说搜索的终止条件是什么?
- 这个递归或者说搜索有什么限制条件?
- 如何递归,又如何回溯?
我们从输入开始讲起:
Saction A 输入
题目中说的很明确,一共有T组样例,这种输入方法在NOIP的考试中很常见,但是有是有很容易产生假死(即循环仍在进行但是在运行窗口中显示什么都没有)的状态。这种输入可以采用while循环(当然for循环也可以)。
一般来说,这种多组数据的输入有主要的两种状态:
- 给定N组数据进行输入,N是输入的第一个数据。
- 同样给定N组数据进行输入,但是N是未知的。
同样的,有两种对应这两种输入的方法。
对于第一种,标准的用循环输入。
使用for循环时,第一个数据N先输入,然后开个for循环,重复N次,循环输入每一组的数据,程序如下:
1 cin>>N; 2 for(int i=1;i<=N;i++) 3 { 4 cin>>... 5 }
使用while循环,同样输入一个数据N然后再while中递减。深入研究while,我们可以发现while后面的括号里装的是一个bool值,本质和if没什么区别,既然是bool值,那么就是一个真与假的判断:一开始输入N时,N可以大于0,也可以等于0。当N大于0时,没做一次循环,N就减一次(N--),直到其值为0,既然是0,那么判断为假,循环自然就结束了;当N等于0,循环在一开始就结束了,也可以理解为这个循环根本没有被执行,为了解决这个问题,就有了do...while()循环,程序如下:
1 cin>>N; 2 while(N--) 3 { 4 cin>>... 5 }
然而在使用这两种输入方法的时候会有一种很别扭的时候:N组数据下每一组还有M组数据,比如这题。这个时候开两个for或者两个while似乎都不太好,其实只要把两种嵌套就行了,嵌套顺序根据个人喜好而定,我个人喜欢在while里面套for。
对于第二种,不用说,肯定是要用while循环的。
很多人对于这种需要用Ctrl+Z来结束输入的题目没有什么好感,最主要的就是这种输入太容易造成runtime error!应对这种输入,我们需要有EOF的概念。所谓EOF就是end of file,通常在文本的最后存储,表示此字符资料结束,在这里换句话说就是输入结束了,而它在ascll中就是Ctrl+Z来表示的。
既然这个EOF存在于输入中(当然题目不可能傻到把“EOF”写到输入文件的末尾),那么只要我们读入到的东西为“EOF”就可以结束输入了。当然,你可能会人为需要用字符串(或字符串数组)来输入,因为“EOF”不是数字,但注意!“EOF”在ascll中是存在的,也就是说用int型的数据也可以读入“EOF”。程序如下:
1 while(scanf("%d",&N)!="EOF") //注意,这输入只能用scanf! 2 {//写cin>>N!="EOF"是绝对错的! 3 cin>>... 4 }
至于有些题目特意强调输入为‘0’或“end”之类的东西结束输入,只用把“EOF”换掉就行了。
Saction B 搜索
讲到这里,我们就不得不统一一下数据名和数据含义:
1 const int minn=INT_MAX; //最小值,即最优答案 2 int T; //应题目要求,共T组数据 3 int n,ans,a[30]; //n应题目要求,ans表示最终答案,a数组表示每人的体重
首先,我们将ans的值定为最大即INT_MAX(这是老套路),接着就是dfs了。
dfs的深度不言而喻,肯定是n,也就是说,如果搜索的深度大于了n,那么肯定要确定答案并且提出函数,如果没有那么就可以继续搜索。
搜索中有五个关键,第一个是左边的人数,第二个是右边的人数,第三个是左边的总重量,第四个是右边的总重量,第五个是搜索深度。这五个值我们分别用lr,rr,ls,rs,x表示。
搜索两次:
- 第一次,搜索右边,深度+1,右边人数+1,右边重量+a[x]。
- 第二次,搜索左边,深度+1,左边人数+1,左边重量+a[x]。
这两次搜索并着写就行了。最关键的还是确定答案。如果x>n,就有两种可能:第一种,两边人数差的绝对值大于1,此时要直接回溯(return),但这种情况不多见。第二种,正常情况,取ans和两边重量差的绝对值两者中的最小值,回溯。
看吧,其实很简单的!
完整代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int minn=INT_MAX; 4 int T,n,ans,a[30]; 5 void dfs(int x,int lr,int rr,int ls,int rs) 6 { 7 if(x>n) 8 { 9 if(abs(lr-rr)>1) 10 return; 11 ans=min(ans,abs(ls-rs)); 12 return; 13 } 14 dfs(x+1,lr,rr+1,ls,rs+a[x]); 15 dfs(x+1,lr+1,rr,ls+a[x],rs); 16 } 17 int main() 18 { 19 cin>>T; 20 while(T--) 21 { 22 ans=minn; 23 cin>>n; 24 for(int i=1;i<=n;i++) 25 cin>>a[i]; 26 dfs(1,0,0,0,0); 27 cout<<ans<<endl; 28 } 29 return 0; 30 }