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);
}








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章