题目地址 : http://ace.delos.com/usacoprob2?S=milk3&a=noSD7NtGmGx
这道题目,其实很简单,但是一开始并没有找到解题思路,因为“误入歧途”。总是试图去用纯数学的方法去解决,换句话说就是去试图找出该问题的规律。但这道题目是被放在USACO简单搜索那一章的,后来无奈用搜索的思路想了一下,茅塞顿开。
像这种最单纯的搜索题目,关键问题是,设计搜索过程中的状态表示,然后建立状态跳转的递归关系,最后再分析递归终止条件。
比如该题目,由于三个桶的总量是不变的,所以(a,b)表示一个中间状态,表示当前第一个桶和第二个桶的牛奶量,那么 c = mc - a - b;(ma,mb,mc 分别表示三个桶的容量)。然后可以分析出,初始状态是(0,0),接受状态为(0,x).
状态跳转也很直观,针对每一次“当前状态”有六种选择,A往B中倾倒,A往C中倾倒,B往A中倾倒,B往C中倾倒,C往A中倾倒,C往B中倾倒。
所以可以通过这道题目可以看到计算机 解决深搜题目的特点,无非就是我们人为地为计算机设置了一套“递归规则”,什么时候开始,如何状态跳转,如何判定结束。然后让计算机做“步步深入”的工作,这个工作我们不如计算机,我们如果“干想”,走不了几步,就会乱套。因为我们的大脑不象计算机,有“栈”机制,可以保存之前的状态。我们大脑厉害的地方是设定规则,计算机的强项是“存储记忆”和“遍历速度”。
/* ID:liuweiv2 PROG:milk3 LANG:C++ */ #include<iostream> #include<fstream> using namespace std; const int W = 21; int ma,mb,mc; bool isOk[W]; bool vis[W][W]; void dfs(int a,int b){ if(vis[a][b]) return; vis[a][b] = true;//该状态的搜索情况做上标记,以防止重复递归。 int c = mc - a - b;//当前状态下的a,b,c if(a == 0) isOk[c] = true; //a->b if(a+b<=mb) dfs(0,a+b); else dfs(a-mb+b,mb); //a->c if(a+c<=mc) dfs(0,b); else dfs(a-mc+c,b); //b->a if(b+a<=ma) dfs(b+a,0); else dfs(ma,b-ma+a); //b->c if(b+c<=mc) dfs(a,0); else dfs(a,b-mc+c); //c->a if(c+a<=ma) dfs(c+a,b); else dfs(ma,b); //c->b if(c+b<=mb) dfs(a,c+b); else dfs(a,mb); } int main(){ ifstream infile; ofstream outfile; infile.open("milk3.in"); outfile.open("milk3.out"); infile>>ma>>mb>>mc; //memset(isOk,0,sizeof(isOk)); //memset(vis,0,sizeof(vis)); for(int i=0;i<W;i++) { isOk[i] = false; for(int j=0;j<W;j++) vis[i][j] = false; } dfs(0,0); bool first = true; for(int i=0;i<=mc;i++) if(isOk[i]){ if(first){ outfile<<i; first = false; } else outfile<<" "<<i; } outfile<<endl; return 0; }