中南大学oj:1336: Interesting Calculator(广搜经典题目)

http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1336

There is an interesting calculator. It has 3 rows of buttons.

Row 1: button 0, 1, 2, 3, ..., 9. Pressing each button appends that digit to the end of the display.

Row 2: button +0, +1, +2, +3, ..., +9. Pressing each button adds that digit to the display.

Row 3: button *0, *1, *2, *3, ..., *9. Pressing each button multiplies that digit to the display.

Note that it never displays leading zeros, so if the current display is 0, pressing 5 makes it 5 instead of 05. If the current display is 12, you can press button 3, +5, *2 to get 256. Similarly, to change the display from 0 to 1, you can press 1 or +1 (but not both!).

Each button has a positive cost, your task is to change the display from x to y with minimum cost. If there are multiple ways to do so, the number of presses should be minimized.

Input

There will be at most 30 test cases. The first line of each test case contains two integers x and y(0<=x<=y<=105). Each of the 3 lines contains 10 positive integers (not greater than 105), i.e. the costs of each button.

Output

For each test case, print the minimal cost and the number of presses.

Sample Input

12 256
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
12 256
100 100 100 1 100 100 100 100 100 100
100 100 100 100 100 1 100 100 100 100
100 100 10 100 100 100 100 100 100 100

Sample Output

Case 1: 2 2
Case 2: 12 3

题意:就是说有x,y两个数,要从x变到y,现在有三行操作,第一行表示在x后面添加一位数,0,1,2,3,4,5,6,7,8,9依次需要的花费,第二行表示x加上一个数,0,1,2,3,4,5,6,7,8,9依次需要的花费,第三行表示x乘一个数,0,1,2,3,4,5,6,7,8,9依次需要的花费,现在要你求最少的花费,要是有多个最少的花费,那么输出步数最少的......

思路:遇到这种题目,最少花费,最多花费,等等之类的,先考虑最短路、搜索,最后才是dp,这个题目由于是点对点的模式,最短路构图的话,比较麻烦,那么就是搜索了,搜索的话,先看状态数,这个题目里面花费就是状态数,题目说了,花费最多为100000,那么也就是说搜索的状态数最大为100000,那么搜索是可行的。若是搜索的话,可以优先考虑广搜,深搜回溯那东东太耗时,并且曾经在写递归的时候,发现单层递归也大概只能是2万层的样子,多了空间没那么大。

好吧,这个题目考虑到这里,广搜明显可行。搜索的话,得优先更新花费小的状态,那么可以引入优先队列。好吧,初期思考到此为止,编完,发现第二组测试数据过不了.......

仔细思考后,发现会存在这样一个问题,由于我在更新完状态,把状态加入队列的时候,就把状态标记了,下次这个状态数就会被直接过滤掉。这里我事先并不能保证这个状态数更新完的时候,加入队列时的价值是最小的,所以这里是不可以标记的,但是如果不标记,明显会出现很多重复的状态,使得暴栈,暴空间,时间复杂度也明显会大上很多......

我是开了一个记录各个状态最小值的数组,如果状态要进入队列,那么它必须比记录的数值要小.......如此,1600ms过了这个题目。

#include<iostream> 
#include<cstdio> 
#include<cstring> 
#include<queue> 
using namespace std; 
struct ss 
{ 
    friend bool operator<(const ss a,const ss b) 
    { 
        if(a.k>b.k) 
            return 1; 
        else  if(a.k==b.k&&a.step>b.step) 
        return 1; 
        else
        return 0; 
    } 
    int x; 
    int step; 
    int k; 
}; 
int n,m; 
int t[4][15]; 
int vist1[100005]; 
int vist[100005]; 
//queue<int>q1; 
void bfs() 
{ 
    priority_queue<ss>q; 
    ss p; 
    p.k=0; 
    p.x=n; 
    p.step=0; 
    vist[n]=0; 
    q.push(p); 
    while(!q.empty()) 
    { 
        p=q.top(); 
        q.pop(); 
        if(vist1[p.x]==1) 
        continue; 
        vist1[p.x]=1; 
        if(p.x==m) 
        { 
            printf("%d %d
",p.k,p.step); 
            return; 
        } 
        for(int i=0; i<3; i++) 
        { 
            for(int j=0; j<=9; j++) 
            { 
                ss p1; 
                p1.step=p.step+1; 
                if(i==0) 
                { 
                    p1.x=p.x*10+j; 
                } 
                if(i==1) 
                { 
                    p1.x=p.x+j; 
                } 
                if(i==2) 
                { 
                    p1.x=p.x*j; 
                } 
                p1.k=p.k+t[i][j]; 
                if(p1.x>=0&&p1.x<=100000&&!vist1[p1.x]&&vist[p1.x]>p1.k) 
                { 
                    vist[p1.x]=p1.k; 
                    //vist[p1.x]=1; 
                    //q1.push(p1.x); 
                    q.push(p1); 
                } 
            } 
        } 
    } 
} 
int main() 
{ 
    int text=0; 
    while(scanf("%d%d",&n,&m)>0) 
    { 
        for(int i=0; i<3; i++) 
        { 
            for(int j=0; j<10; j++) 
            { 
                scanf("%d",&t[i][j]); 
            } 
        } 
        //memset(vist,0,sizeof(vist)); 
        for(int i=0;i<=100000;i++) 
        vist[i]=(1<<29); 
        memset(vist1,0,sizeof(vist1)); 
        printf("Case %d: ",++text); 
        bfs(); 
    } 
    return 0; 
} 
原文地址:https://www.cnblogs.com/ziyi--caolu/p/3476716.html