poj1606 BFS

/**
 * poj1606 BFS
 * 这个题看到的两种主流的方法,一个是BFS,另一个是根据状态去按序输出,类似于模拟
 * 模拟的方法应该速度会更快,但是需要有足够的证明才好,正好我还没有怎么练过BFS,就先按BFS的方法做吧
 * 用BFS竟然给了0ms,看来测试用例还是比较简单的
 * BFS的思路和DFS不太一样,DFS的实现是递归,而BFS的实现是循环,并建立新的节点
 * 这道题的思路就是,一共6种操作,从原点开始,对当前状态分别进行这6种操作,并将操作后的状态检查是否已经出现过
 * 如果这个状态还没有出现过,就将其作为一个新的节点(包括a和b的状态,当前的操作,以及它的父节点是谁)记录下来。这个不断产生新节点的过程就是BFS
 * 按照操作次数的升序依次对节点进行遍历,直到满足b中的水量与需求的一致,退出循环,回溯得到操作顺序,并将其打印就行了 
 * 
 * 关于ca和cb互素,则题目目标一定能达到的证明,从网上抄了别人的一段,用到了数论的一个小定理
一个很大家熟悉的题目:
有两个量杯,一个容量5升,另一个容量3升; 还有无穷多的自来水(不过,请节约用水,这是美德)。
问题是:现在仅依靠两个量杯通过倒水的方式量出4升水来(量杯里头的水可以自由倒进倒出)。

推广到一般情况:换成 M 升 和 N 升( M>N ,且M、N同时为自然数)的两个量杯呢? :ask
显然未必能量出1~M升的水。不过其中的规律如何,大家说说看

命题:对于所有最大公因数为1 的A,B,都能产生任意的1~max(A,B)之间的容量,反之则不成立。
证明如下:
先证明一个需要用到的数论中的定理
如果a,b为任意整数,但不全为0,那么a,b的最大公约数为满足s = ax+by (x,y 为任意整数)的最小的正整数s。
证明如下:令s = ax + by (x,y为任意整数)的最小正整数,令q=floor(a/s),那么有如下等式:
代码:a mod s = a - qs = a - q(ax + by) = a(1 - px) + b(-qy)可见a mod s的值也是a与b的线性组合,
但a mod s < s所以a mod s只能为0, 同理b mod s也为0,所以s为a,b的公约数,所以有***(a,b) >= s;但是,因为s = ax + by并且有(ax + by)能被***(a,b)整除:
代码:因为a,b的最大公约数为***(a,b),所以有a = p****(a,b);b = q****(a,b);ax+by = x*p****(a,b)+y*q****(a,b) = (x*p+y*q)****(a, b)也就是***(a,b)|(ax+by)

所以有***(a,b) <= s所以s = ***(a,b),此命题得证。
下面证明初始命题:
因为A,B互素(也就是最大公约数为1)所以***(A,B) = 1,那么根据上面的定理,有存在整数x,y使得***(A,B) = Ax + By ,且最小,
也就是说***(A,B)是满足整数个A,与整数个B的和的最小正整数值,这里我们很清楚的看到,整数个A,整数个B的和肯定是一个为正一个为负(对于本题来说),
设A为正,B为负,那么也就是说通过把x桶A,放到y桶B中,就可以得到***(A,B)的容量,而这正是通过操作可以达到的最小容量,
如果此容量不为1,那么1是无论如何都不可能出现的,而只要产生了1,那么自然可以产生任意的容量。所以原命题的证。
 */
#include <cstdio>

bool state[1001][1001];
char output[1001*1001];//0 - fill A; 1 - fill B; 2 - empty A; 3 -empty B; 4 - pour A B; 5 - pour B A
char sts[6][10] = {"fill A","fill B","empty A","empty B","pour A B","pour B A"};

int ca,cb,n;
int father,child;

struct node{
    int a;
    int b;
    int pre;
    int op;
} nod[1001*1001];

int min(const int a, const int b){
    return a>b ? b : a;
}

void print(int x){
    int cnt = 0;
    while(x){
        output[cnt++] = nod[x].op;
        x = nod[x].pre;
    }
    for(int i=cnt-1;i>=0;--i){
        printf("%s\n",sts[output[i]]);
    }
}

void newnode(int xa,int xb,int op,int father,int& idx){
    if(!state[xa][xb]){
        state[xa][xb] = true;
        nod[idx].a = xa;
        nod[idx].b = xb;
        nod[idx].pre = father;
        nod[idx].op = op;
        ++idx;
    }
}


void bfs(){
    nod[0].a = 0;
    nod[0].b = 0;
    state[0][0] = true;

    father = 0;
    child = 1;

    while(1){
        if(nod[father].b == n){
            print(father);
            printf("success\n");
            return;
        }

        int xa,xb,x;

        //fill A
        if(nod[father].a!=ca){
            xa = ca;
            xb = nod[father].b;
            newnode(xa,xb,0,father,child);            
        }

        //fill B
        if(nod[father].b!=cb){
            xa = nod[father].a;
            xb = cb;
            newnode(xa,xb,1,father,child);            
        }

        //empty A
        if(nod[father].a!=0 && nod[father].b!=0){
            xa = 0;
            xb = nod[father].b;
            newnode(xa,xb,2,father,child);            
        }

        //empty B
        if(nod[father].a!=0 && nod[father].b!=0){
            xa = nod[father].a;
            xb = 0;
            newnode(xa,xb,3,father,child);           
        }

        //pour A B
        if(nod[father].b!=cb){
            x = min(cb - nod[father].b, nod[father].a);
            xa = nod[father].a - x;
            xb = nod[father].b + x;
            newnode(xa,xb,4,father,child);            
        }

        //pour B A
        if(nod[father].a!=ca){
            x = min(ca - nod[father].a, nod[father].b);
            xa = nod[father].a + x;
            xb = nod[father].b - x;        
            newnode(xa,xb,5,father,child);            
        }

        ++father;
    }
}

int main(){
    while(scanf("%d%d%d",&ca,&cb,&n) != EOF){
        for(int i=0;i<=ca;++i){
            for(int j=0;j<=cb;++j){
                state[i][j] = false;
            }
        }

        bfs();
    }    

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值