另外一道题目:点菜

来源:互联网 发布:adobe xd mac 破解版 编辑:程序博客网 时间:2024/06/10 07:52

这个的原体比较长,方便起见,也摘抄在这里吧。

饭团的烦恼
“午餐饭团“是百度内部参与人数最多的民间组织。
同一个部门的,同一间大学的,同一年出生的,用同一种型号电脑的,员工们总是以各种理由,各种借口组织各种长久的,临时的饭团。
参加饭团,不仅可以以优惠的价格尝到更加丰富的菜式,还可以在吃饭的时候和同事们唠唠嗑,吹吹水,增进感情。
但是,随着百度的员工越来越多,各个饭团的管理随即变得烦杂。特别是为了照顾员工们越来越挑剔的胃口,饭团的点菜负责人背负的责任越来越大。现在,这个重担落在百度之星的肩上,因为,你们将要为所有的百度饭团设计一个自动点菜的算法。
饭团点菜的需求如下:
1. 经济是我们要考虑的一个因素,既要充分利用百度员工的午餐补助,又不能铺张浪费。因此,我们希望最后的人均费用越接近12元越好。
2. 菜式丰富是我们要考虑的另一个因素。为简单起见,我们将各种菜肴的属性归结为荤菜,素菜,辛辣,清淡,并且每个菜只能点一次。
3. 请紧记,百度饭团在各大餐馆享受8折优惠。
输入数据描述如下:
第一行包含三个整数N,M,K(0<=16,0<=16,0<=N,0<=N,0<=12),分别表示菜单上菜的数目,饭团需要点的菜的数目,就餐的人数。<=12),分别表示菜单上菜的数目,饭团需要点的菜的数目,就餐的人数。
紧接着N行,每行的格式如下:
菜名(长度不超过20个字符) 价格(原价,整数) 是否荤菜(1表示是,0表示否) 是否辛辣(1表示是,0表示否)例:
水煮鱼 30 1 1
紧接着是a b c d 四个整数,分别表示需要点的荤菜,素菜,辛辣,清淡菜的数目。
输出数据:
对于每一测试数据,输出数据包含M+1行,前M行每行包含一个菜名(按菜名在原菜单的顺序排序)。第M+1行是人均消费,结果保留两位小数。
说明:
1.结果菜单的数目应该恰好为M,荤菜,素菜,辛辣,清淡菜的数目恰好为a,b,c,d。在满足这样的前提下,选择人均消费最接近12元的点菜方案。题目数据保证有且仅有一个解。
2.每组测试数据的结果用一个空行隔开。末尾不要有多余的空行。
输入样例
3 2 2
水煮鱼 30 1
1 口水鸡 18 1 1
清炖豆腐 12 0 0
1 1 1 1
输出样例
口水鸡
清炖豆腐
12.00
时间要求:1S之内

这也是一个简单的线性规划的问题,动动笔就可以写出约束条件和目标值,下面是一个实现,做了一些简化,因为傍晚了得为加班的老婆准备晚饭了。不过我没有测试很多的数据,不知道有没有其他的错误。将来有兴趣了再来弄吧。

假设是荤和素,辛辣和清单都是两者取其一,适当的改了一下输入的格式和数据,因为我没有用文件来写,如下是一个运行的例子:

Please input the food one every line:(name price meat/no-meat pungunt/no-pungunt
)
水煮鱼 30 1 1
口水鸡 18 1 1
清炖豆腐 12 0 0
q
Please input the totalFood,totalMeat,totalPungunt,totalPeople:
2 1 1 2

You should total price is:30, the menu:
口水鸡
清炖豆腐

以下是源代码:

#include
#include

using namespace std;

#define MAX_FOOD_NUM 12
typedef struct {
    char name[20];
    float price;
    int isMeat;
    int isPungunt;
}FOOD;

vector g_food;

int g_totalFood = 0; /*the sum of the food should be ordered*/
int g_totalMeat = 0;
int g_totalPungunt = 0;
int g_totalPeople = 0;

/*
*  after *it, calculate the best total price.
*  return the best price.
* meat is the total meat we can selected from the behind food, the same is pungunt and food
* */
float orderFood(vector::iterator it, int meat, int pungunt, int food, float bestPrice, vector &bestOrder)
{
    vector::iterator iter = it;
    float priceYes=-1, priceNo=-1; // the best prices we can get if order or not, not include this food's price.
    vector yesOrder, noOrder;
    // at first, we check that if we arrive at the end of the menu
    if(it == g_food.end())
    {
        if(meat!=0 || pungunt!=0 || food!=0)
        {
            // shows that the previous order is not valid
            return -1;
        }
    }
    if(food == 0 && (meat!=0 || pungunt!=0))
    {
        return -1;
    }
    if(food == 0 && meat==0 && pungunt==0)
    {
        return 0;
    }
    // if we can order this food in the case of quota
    if(meat-iter->isMeat>=0 && pungunt-iter->isPungunt >=0
       && food>0 )
    {
        priceYes = orderFood(iter+1, meat-iter->isMeat, pungunt-iter->isPungunt, food-1, bestPrice-iter->price, yesOrder);
    }
    // if we don't order this food
    priceNo = orderFood(iter+1, meat, pungunt, food, bestPrice, noOrder);
    if(priceYes<0 && priceNo>=0)
    {
        // we should select priceNo as the best one
        bestOrder.push_back(0);
        bestOrder.insert(bestOrder.end(), noOrder.begin(), noOrder.end());
        return priceNo;
    }
    else if(priceYes>=0 && priceNo<0)
    {
        bestOrder.push_back(1); //add it self first
        bestOrder.insert(bestOrder.end(), yesOrder.begin(), yesOrder.end());
        return priceYes+iter->price;
    }
    else if(priceYes<0 && priceNo<0)
    {
        return -1; // this order is invalid
    }
    // order or not are valid
    float yes = (priceYes+iter->price-bestPrice)>0?(priceYes+iter->price-bestPrice):(-priceYes-iter->price+bestPrice);
    float no = (priceNo-bestPrice)>0?(priceNo-bestPrice):(-priceNo+bestPrice);
    if(yes < no)
    {
        bestOrder.push_back(1);
        bestOrder.insert(bestOrder.end(), yesOrder.begin(), yesOrder.end());
        return priceYes+iter->price;
    }
    else{
        bestOrder.push_back(0);
        bestOrder.insert(bestOrder.end(), noOrder.begin(), noOrder.end());
        return priceNo;
    }   
}

int main(void)
{
    FOOD food;
    float price;
    vector order;
    cout<<"Please input the food one every line:(name price meat/no-meat pungunt/no-pungunt)"<
    food.name[0]=0;
    cin>>food.name;
    while(food.name[0]!='q')
    {
        cin>>food.price>>food.isMeat>>food.isPungunt;
        g_food.push_back(food);
        cin>>food.name;
    }
    cout<<"Please input the totalFood,totalMeat,totalPungunt,totalPeople:"<
    cin>>g_totalFood>>g_totalMeat>>g_totalPungunt>>g_totalPeople;
    price = orderFood(g_food.begin(), g_totalMeat, g_totalPungunt, g_totalFood, float(g_totalPeople)*12.0, order);
    vector::iterator it=order.begin();
    vector::iterator itFood = g_food.begin();
    cout<<"/nYou should total price is:"<<<", the menu:"<
    while(it!=order.end())
    {
        if(*(it++) == 1)
        {
            cout<name<
        }
        itFood++;
    }
    return 0;
}

视线中遇到的问题也有几个。首先是如何保存最佳的选择,一开始我是用每个FOOD结构里面弄一个selected变量来表示,在yes/no的分支中分别置上和取消,以为程序的迭代都是对后面的FOOD影响,对当下的元素应该没有问题,没想到还是不行。因为忽略了一点,假设在yes的时候设置了后面某个变量的selected,并且这是最佳的选择,但是后面的no分支可能就把这个变量的selected给复位了。所以这里用到了一个bestOrder参数用来保存在选择当下变量时的最佳选择,然后和FOOD结构一一对应起来。这里利用函数的参数,作为一个临时变量,而不是都去操作一个全局的FOOD vector,就可以避免被冲掉的危险

这似乎也是一个0/1背包问题。觉得对于这些递归,迭代的应用,一个关键是定义好函数的含义(一个适当的定义非常重要,不单单是这种背包问题,在动态规划中更是如此,这也是一个数学功底?),然后写出伪代码整体思路。