hdu1459_非常可乐

题目大意:

有一杯满可乐,容量为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;
}

伟大的梦想成就伟大的人,从细节做好,从点点滴滴做好,从认真做好。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值