uva 10603 Fill

题目地址:
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1544

题目描述:

There are three jugs with a volume of a, b and c liters. (a, b, and c are positive integers not greater than 200). The first and the second jug are initially empty, while the third

is completely filled with water. It is allowed to pour water from one jug into another until either the first one is empty or the second one is full. This operation can be performed zero, one or more times.

You are to write a program that computes the least total amount of water that needs to be poured; so that at least one of the jugs contains exactly d liters of water (d is a positive integer not greater than 200). If it is not possible to measure d liters this way your program should find a smaller amount of water d' < d which is closest to d and for which d' liters could be produced. When d' is found, your program should compute the least total amount of poured water needed to produce d' liters in at least one of the jugs.

Input

The first line of input contains the number of test cases. In the next T lines, T test cases follow. Each test case is given in one line of input containing four space separated integers - a, b, c and d.

 

Output

The output consists of two integers separated by a single space. The first integer equals the least total amount (the sum of all waters you pour from one jug to another) of poured water. The second integer equals d, if d liters of water could be produced by such transformations, or equals the closest smaller value d' that your program has found.

Sample Input

Sample Output

2

2 3 4 2

96 97 199 62

2 2

9859 62



题意:
有这样三杯水(没有刻度,你只能讲一杯倒满或者另一杯倒空) 一开始前两个杯子是空的,第三个杯子是满的。问给
出一个数d,通过不断地倒杯子水,使恰好有一个杯子有d升水或者 次小的d1升水 d1 是 次小的 d。问通过最小的倒水数(体积数)得到这样一个d或者d1.


题解:
很经典的隐式图遍历的题,讲三个杯子的每一个状态看成一个节点。
比如 样例中的 0 , 0 , 199 节点 可以有两个孩子节点  96  0  103  和 0   97   102
然后两个孩子继续向下拓展更多孩子节点,找到一个,一个杯子的水接近d且权重(这里是多少升水)最小的节点即为目标解。
所以此题的的模型为:
遍历一个多叉树 ,找到 一个节点的a or b or c 最接近d 且 权重最小的点。
我这里的做法是:
BFS(广搜)这棵树,然后边搜索边累计计算权值边选择调整最优解,并记忆化已经搜索过的点(作剪枝用)。当遍历完整棵树后,最优解就找到了。

这里需要注意的是:
1、虽然是BFS广搜,但是不能讲找到最接近的d或者最小的权值片面地决定这就是最优解,因为这里权值是多少升水并不是树的路径长度,所以不能像常规广搜那样,找到一个是d升水的节点就退出整个搜索。
2、我这里做了记忆化处理,为的是剪枝和搜索的不可逆(搜索可逆死循环),这里记忆化的排斥(我代码中的is_exist() 就是记忆化的排斥动作)不能是,a,b,c相同的就排斥而是 a,b,c相同且 一路下来倒的水升数比我记忆的点的倒的水的升数还要多,那么我们必定剪掉这个分支,因为这个分支肯定是产生不了的最优解了(反证法证明 我们剪掉的这个分支产生的解肯定是次优于记忆化里记录的分支的)
3、注意样例之间的数据清理,包括树结构(我这里是队列来存树结构)清理,记忆化堆栈清理等等。
4、注意最有解的比较 有两个要素影响  (1)接近于d的程度 (2)权重最小,(1)的优先级比(2)大,所以我一定是先比较d,尽量取接近于d的节点 如果接近d的程度(d-d1的差值可以衡量程度)相同,我们再去比较权值的大小(越小越好)。 


代码:

#include <stdio.h>
#define MM 99999999
#define DM 1000000
typedef struct node 
{
    int a;
    int b;
    int c;
    int poured;
    struct node * next;
    node()
    {
        a=0;
        b=0;
        c=0;
        poured=0;
        next = NULL;
    }
}node, * node_lnik;
node * head = NULL;
node * rear = NULL;
int a,b,c,d,d1;//d1 <= d
int t;
node * history[DM];//add the all node no repeat node , no same node will add to the queue and stack
int top;
node * reached_node;
void push(node * elem)
{
    if(elem == NULL) return;
    if(head == NULL || rear == NULL)
    {
        head = elem;
        rear = elem;
        elem->next = NULL;
    }
    else
    {
        rear->next = elem;
        elem->next = NULL;
        rear = elem;
    }
}
node * pop()
{
    if(head == NULL || rear == NULL)
    {
        return NULL;
    }
    else
    {
        if(head == rear)//only 1 elem
        {
            node * ret = head;
            head = rear = NULL;
            return ret;
        }
        else
        {
            node * ret = head;
            head = head->next;
            return ret;
        }
    }
}
void nodecpy(node & n1, node & n2)
{
    n1.a = n2.a;
    n1.b = n2.b;
    n1.c = n2.c;
    n1.poured = n2.poured;
    n1.next = n2.next;
}
void push_stack(node elem)
{
    node * new_elem = new node();
    nodecpy(*new_elem, elem);
    history[top++] = new_elem;
}
bool is_equal(node n1, node n2)
{
    if(n1.a == n2.a && n1.b == n2.b && n1.c == n2.c) return true;
    else return false;
}
bool is_exist(node n)
{
    for(int i = 0; i <= top - 1; i++)
    {
        if(is_equal(*history[i], n))
        {
            if(n.poured >= history[i]->poured) return true;
        }
    }
    return false;
}
bool is_reached(node * elem)
{
    if(elem == NULL) return false;
    int dd = 0;//dd is max of { (a,b,c) < d }
    if(elem->a == d || elem->b == d || elem->c == d)
    {
        if(d1 == d)
        {
            if(elem->poured < reached_node->poured) nodecpy(*reached_node, *elem);
        }
        else
        {
            d1 = d;
            nodecpy(*reached_node, *elem);
        }
        return true;
    }
    else
    {
        if(elem->a < d && elem->a >= d1)
        {
            if(elem->a > d1)
            {
                d1 = elem->a;
                nodecpy(*reached_node, *elem);
            }
            else//elem->a == d1
            {
                if(elem->poured < reached_node->poured) nodecpy(*reached_node, *elem);
            }
        }
        if(elem->b < d && elem->b >= d1)
        {
            if(elem->b > d1)
            {
                d1 = elem->b;
                nodecpy(*reached_node, *elem);
            }
            else
            {
                if(elem->poured < reached_node->poured) nodecpy(*reached_node, *elem);
            }
        }
        if(elem->c < d && elem->c >= d1)
        {
            if(elem->c > d1)
            {
                d1 = elem->c;
                nodecpy(*reached_node, *elem);
            }
            else
            {
                if(elem->poured < reached_node->poured) nodecpy(*reached_node, *elem);
            }
        }
        return false;
    }
}
bool is_queue_empty()
{
    if(head == NULL && rear == NULL) return true;
    else return false;
}
void clear_queue()
{
    if(head == NULL && rear == NULL) return;
    else
    {
        node * pot = head;
        while(pot!=NULL)
        {
            node * temp = pot;
            pot = pot->next;
            delete temp;
            temp = NULL;
        }
        head = rear = NULL;
    }
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d%d", &a, &b, &c, &d);
        //initialize the queue
        top = 0;
        d1 = 0;
        reached_node = new node();
        reached_node->poured = MM;
        node * elem = new node();
        elem->a=0;
        elem->b=0;
        elem->c=c;
        node his;
        nodecpy(his, *elem);
        push(elem);
        push_stack(his);
        if(is_reached(elem))
        {
            printf("%d %d\n",reached_node->poured, d1);
            clear_queue();
            delete reached_node;
            reached_node = NULL;
            continue;
        }
        while(!is_queue_empty())
        {
            int flag=0;
            elem = pop();
            //generate children that is to say brute pour maney ways to generate many children
            //a pour to b or c
            if(elem->a > 0)
            {
                //pour to b
                if(elem->b < b)
                {
                    node * new_elem = new node();
                    new_elem->b = elem->b + elem->a;
                    if(new_elem->b > b)
                    {
                        int p = b - elem->b;
                        new_elem->a = elem->b + elem->a - b;
                        new_elem->b = b;
                        new_elem->c = elem->c;
                        new_elem->poured = elem->poured + p;
                    }
                    else//elem->a is 0
                    {
                        int p = elem->a;
                        new_elem->a = 0;
                        new_elem->c = elem->c;
                        new_elem->poured = elem->poured + p;
                    }
                    if(!is_exist(*new_elem))
                    {
                        if(is_reached(new_elem)) flag=1;
                        push(new_elem);
                        push_stack(*new_elem);
                    }
                    else delete new_elem;
                }
                //pour to c
                if(elem->c < c)
                {
                    node * new_elem = new node();
                    new_elem->c = elem->c + elem->a;
                    if(new_elem->c > c)
                    {
                        int p = c - elem->c;
                        new_elem->a = elem->c + elem->a - c;
                        new_elem->b = elem->b;
                        new_elem->c = c;
                        new_elem->poured = elem->poured + p;
                    }
                    else
                    {
                        int p = elem->a;
                        new_elem->a = 0;
                        new_elem->b = elem->b;
                        new_elem->poured = elem->poured + p;
                    }
                    if(!is_exist(*new_elem))
                    {
                        if(is_reached(new_elem)) flag=1;
                        push(new_elem);
                        push_stack(*new_elem);
                    }
                    else delete new_elem;
                }
            }
            //b pour to a or c
            if(elem->b > 0)
            {
                //pour to a
                if(elem->a < a)
                {
                    node * new_elem = new node();
                    new_elem->a = elem->a + elem->b;
                    if(new_elem->a > a)
                    {
                        int p = a - elem->a;
                        new_elem->a = a;
                        new_elem->b = elem->a + elem->b - a;
                        new_elem->c = elem->c;
                        new_elem->poured = elem->poured + p;
                    }
                    else
                    {
                        int p = elem->b;
                        new_elem->b = 0;
                        new_elem->c = elem->c;
                        new_elem->poured = elem->poured + p;
                    }
                    if(!is_exist(*new_elem))
                    {
                        if(is_reached(new_elem)) flag=1;
                        push(new_elem);
                        push_stack(*new_elem);
                    }
                    else delete new_elem;
                }
                //pour to c
                if(elem->c < c)
                {
                    node * new_elem = new node();
                    new_elem->c = elem->c + elem->b;
                    if(new_elem->c > c)
                    {
                        int p = c - elem->c;
                        new_elem->a = elem->a;
                        new_elem->b = elem->c + elem->b - c;
                        new_elem->c = c;
                        new_elem->poured = elem->poured + p;
                    }
                    else
                    {
                        int p = elem->b;
                        new_elem->a = elem->a;
                        new_elem->b = 0;
                        new_elem->poured = elem->poured + p;
                    }
                    if(!is_exist(*new_elem))
                    {
                        if(is_reached(new_elem)) flag=1;
                        push(new_elem);
                        push_stack(*new_elem);
                    }
                    else delete new_elem;
                }
            }
            //c pour to a or b
            if(elem->c > 0)
            {
                //pour to a
                if(elem->a < a)
                {
                    node * new_elem = new node();
                    new_elem->a = elem->a + elem->c;
                    if(new_elem->a > a)
                    {
                        int p = a - elem->a;
                        new_elem->a = a;
                        new_elem->b = elem->b;
                        new_elem->c = elem->a + elem->c - a;
                        new_elem->poured = elem->poured + p;
                    }
                    else
                    {
                        int p = elem->c;
                        new_elem->b = elem->b;
                        new_elem->c = 0;
                        new_elem->poured = elem->poured + p;
                    }
                    if(!is_exist(*new_elem))
                    {
                        if(is_reached(new_elem)) flag=1;
                        push(new_elem);
                        push_stack(*new_elem);
                    }
                    else delete new_elem;
                }
                //pour to b
                if(elem->b < b)
                {
                    node * new_elem = new node();
                    new_elem->b = elem->b + elem->c;
                    if(new_elem->b > b)
                    {
                        int p = b - elem->b;
                        new_elem->a = elem->a;
                        new_elem->b = b;
                        new_elem->c = elem->b + elem->c - b;
                        new_elem->poured = elem->poured + p;
                    }
                    else
                    {
                        int p = elem->c;
                        new_elem->a = elem->a;
                        new_elem->c = 0;
                        new_elem->poured = elem->poured + p;
                    }
                    if(!is_exist(*new_elem))
                    {
                        if(is_reached(new_elem)) flag=1;
                        push(new_elem);
                        push_stack(*new_elem);
                    }
                    else delete new_elem;
                }
            }
            delete elem;
            elem = NULL;
        }
        printf("%d %d\n",reached_node->poured, d1);// first compare d1  then  compare poured
        clear_queue();
        delete reached_node;
        reached_node = NULL;
    }
    return(0);
}








猜你喜欢

转载自blog.csdn.net/hackerwin7/article/details/43676633