题目大意:
有一杯满可乐,容量为S个单位,另外有两个空杯子,容量分别为N,M。寻问是否存在一种杯的方法,使得在两个杯子,各有S/2的容量。如果存在,请输出你倒的最少次数。
题目测试数据与数据范围:
7 4 3 4 1 3 0 0 0
NO 3很明显对于 S%2==1 的无法分出半杯,可直接输出NO,而对于 4 1 3,可采取(4,0,0)->(1,0,3)->(1,1,2)->(2,0,2).共三步。
其中 0<S<101 且 S==N+M;
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1495
题目分析:
首先可以确定这是从一个状态转移到另一个状态,初如状态为 (S,0,0) ,目标状态为 (S/2,S/2,0) 或者(S/2,0,S/2);而对于一个状态要发生变化,总共可有六种倒的方法,S->N,S->M,N->S,N->M,M->S,M->N; 对于第一种状态它的下一层次最多为六次,因为可能会存在有重复的状态。所以我们确定这一题是可以用广度优先搜索解决的。但是如何去重呢?要用一个三维数组去标记吗?我们知道,当两个杯子的状态都确定的时候,第三个也就一定确定了,所以用一个二维就够了。所以,在我看来,这又只是一道广度优先搜索的模板题。但其中还真的有一些细节需要你去注意,不注意,很容易出现意想不到的结果,有时候结果都不会出现。因为我们是枚举任意两个杯子相互之间倒水,很容易把第三个没有操作的杯子给忽略。!!!!。
小乐一下:
结点进入队列
(是否要标记起点(优先队列))
进入循环(){
一个结点出队,
(要判断这个是不是目标点吗?)
对于这个可扩展的结点入队(是否要标记)
在扩展的时候(一个状态的所在属性要确保有值),碰到目标点,是进队还是直接返回结束。
}
循环里没有返回,表示没有搜索到目标点,返回-1表示失败。
对于队列,我们有时要考虑自己写的队列,这样有两个好处,其一,可以防止爆栈,其二,在要打印路径的时候,很容易且很有效果。
代码,更好更简洁的由你来实现,不要直接复制。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 105
int vis[maxn][maxn],cap[3],target;
struct Node{
int v[3];
int fa,dist;
};
Node q[maxn*maxn]; //相当于自己定义的一个队列。
int mins(int a,int b){return a<b?a:b;}
void print_path(int idx){ //这个是打印路径。
if(q[idx].fa != idx) print_path(q[idx].fa);
printf("%d %d %d\n",q[idx].v[0],q[idx].v[1],q[idx].v[2]);
}
int bfs(){
int front = 0,rear = 0;
int i,j;
Node cur,next;
q[0].v[0] = cap[0];
q[0].v[1] = q[0].v[2] = q[0].dist = 0;q[0].fa = 0;
vis[cap[0]][0] = 1;
while(front<=rear){
cur = q[front];
int cnt = 0;
if(cur.v[0] == target) cnt++;
if(cur.v[1] == target) cnt++;
if(cur.v[2] == target) cnt++;
if(cnt>=2 ){
//print_path(front); //达到目标点了,该返回了。
return cur.dist;
}
for(i = 0;i<3;i++){
for(j = 0;j<3;j++)if(i!=j){
int temp = mins(cur.v[i],cap[j]-cur.v[j]);
for(int k = 0;k<3;k++) next.v[k] = cur.v[k]; //为了防止我们忽略那个没有进行操作的杯子。
next.v[i] -= temp; //<span style="font-family: Arial, Helvetica, sans-serif;">我们还是先把该有的属性先填上一个与上一个状态有联系的值吧。</span>
next.v[j] += temp;
if(!vis[next.v[0]][next.v[1]]){
vis[next.v[0]][next.v[1]] = 1;
next.dist = cur.dist+ 1;
next.fa = front;
q[++rear] = next;
}
}
}
front ++;
}
return -1;
}
int main(){
int i,j;
while(scanf("%d%d%d",&cap[0],&cap[1],&cap[2])!=EOF){
if(cap[0]+cap[1]+cap[2]==0) break;
if(cap[0]%2) {printf("NO\n");continue;}
target = cap[0]/2;
memset(vis,0,sizeof(vis));
int ans = bfs();
if(ans != -1) printf("%d\n",ans);
else printf("NO\n");
}
return 0;
}
伟大的梦想成就伟大的人,从细节做好,从点点滴滴做好,从认真做好。