回 帖 发 新 帖 刷新版面

主题:百度Astar2006程序设计大赛预赛题--饭团的烦恼

2.饭团的烦恼 
“午餐饭团”是百度内部参与人数最多的民间组织。
同一个部门的、同一所大学的、同一年出生的、使用同一种型号电脑的员工们总是以各种理由组织各种长期的、临时的饭团。


参加饭团,不仅可以以优惠的价格尝到更加丰富的菜式,还可以在吃饭的时候和同事们增进感情。
但是,随着百度的员工越来越多,各个饭团的管理变得繁杂起来。特别是为了照顾员工们越来越挑剔的胃,饭团的点菜负责人的压力也越来越大。现在,这个任务就交给“百度之星”了,因为,你将要为所有的百度饭团设计一个自动点菜的算法。


饭团点菜的需求如下:
1.经济是我们要考虑的一个因素,既要充分利用百度员工的午餐补助,又不能铺张浪费。因此,我们希望最后的人均费用越接近12元越好。
2.菜式丰富是我们要考虑的另一个因素。为简单起见,我们将各种菜肴的属性归结为荤菜,素菜,辛辣,清淡,并且每个菜只能点一次。
3.请谨记,百度饭团在各大餐馆享受8折优惠。


输入要求:
1.输入数据第一行包含三个整数N,M,K(0<N<=16,0<M<=N,0<K<=12),分别表示菜单上菜的数目,饭团需要点的菜的数目,就餐的人数;
2.紧接着N行,每行的格式如下:
菜名(长度不超过20个字符) 价格(原价,整数) 是否荤菜(1表示是,0表示否) 是否辛辣(1表示是,0表示否);
3.第N+2行是 a b c d 四个整数,分别表示需要点的荤菜,素菜,辛辣,清淡菜的数目。例:
3 2 2
水煮鱼 30 1 1
口水鸡 18 1 1
清炖豆腐 12 0 0
1 1 1 1



输出要求:
对于每组测试数据,输出数据包含M+1行,前M行每行包含一个菜名(按菜名在原菜单的顺序排序)。第M+1行是人均消费,结果保留两位小数。例:
口水鸡
清炖豆腐
12.00


评分规则:
1.程序将运行在一台Linux机器上(内存使用不作严格限制),在每一测试用例上运行不能超过10秒,否则该用例不得分;
2.要求程序能按照输入样例的格式读取数据文件,按照输出样例的格式将运行结果输出到标准输出上。如果不能正确读入数据和输出数据,该题将不得分;
3.该题目共有5个测试用例,每个测试用例为一个输入文件。各测试用例占该题目分数的比例分别为20%,20%,20%,20%,20%;
4.该题目10分。

回复列表 (共9个回复)

沙发

#include <iostream.h>
#include <math.h>

/*
文件描述: 解百度赛题第二题
创建人:   陈泽丹  (2006年5月27日)
版本号:   1.0
修改次数: 0

补充说明: 由于还没学到文件流,故只好把测试数据用改为手工输入的方法,有点不合题目。
另外弄了半天^-^!,还是不知怎么在DOS里输入汉字,结果只好以数字代替菜单。
此外,由于需点的菜数量是固定的,而又非荤则素, 非辛则淡,故最后一行数据只需读两个就够了.
*/

const int N_Max=16;
const int M_Max=16;
const int K_Max=12;
const int tot=500;
int number[N_Max];
int cord[M_Max];
int total[tot][M_Max];
int Max;
int len;
int person;
double price[N_Max]={30,18,12};
int meat[N_Max]={1,1,0};
int acridity[N_Max]={1,1,0};
int p=0;
int count=0;
int sum_meat=0;
int sum_acridity=0;

void Init()
{
    int N, M, K;
    cout<<"请输入分别表示菜单上菜的数目:";
    cin>>N;
    cout<<"请输入饭团需要点的菜的数目:";
    cin>>M;
    cout<<"请输入就餐的人数:";
    cin>>K;

    Max=N;
    len=M;
    person=K;
    for (int i=0; i<N; i++)
    {
        number[i]=i;
        cout<<"请输入菜式"<<i+1<<"的价格, 是否为荤菜(1为是,0为否), 是否为辛辣(1为是,0为否)"<<endl;
        cin>>price[i];
        cin>>meat[i];
        cin>>acridity[i];
    }
}

void set(int m, int n , int me_limit, int ac_limit)
{
    int i;
    if (m == len )
    {
        for (i=0; i<len; i++)
        {
            total[p][i]=cord[i];
        }
        count++;
        p++;
    }
    else if ( n < Max)
    {
        for (i=0; i<2; i++)
        {
            if (i==0) 
            {
                if ( meat[n]+sum_meat <= me_limit 
                    && 
                    acridity[n]+sum_acridity <= ac_limit)
                {
                    sum_meat+=meat[n];
                    sum_acridity-=acridity[n];
                    cord[m]=number[n]; 
                    set(m+1,n+1, me_limit, ac_limit);
                    sum_meat-=meat[n];
                    sum_acridity-=acridity[n];
                }
            }
            else { set(m,n+1, me_limit, ac_limit); }
        }
    }
}

int answer()
{
    double money=0;
    double temp=0;
    int k=0;
    for (int i=0; i<count; i++)
    {
        for (int j=0; j<len; j++)
        {
            temp+=price[total[i][j]];
        }
        if (money ==0 ) { money=temp; k=i; }
        else if (abs(temp-12) < abs(money-12) )
        {
            money=temp;
            k=i;
        }
        temp=0;
    }
    return k;
}

void output(int mm)
{
    cout<<"请点以下菜式:"<<endl;
    double money=0;
    for (int i=0; i<len; i++)
    {
        cout<<total[mm][i]<<endl;
        money+=price[total[mm][i]];
    }
    cout<<money*0.8/person<<endl;
}
void main()
{
    count=0;
    Init();
    int a,b, c, d;
    cout<<"请输入需要点的荤菜,素菜,辛辣,清淡菜的数目:"<<endl;
    cin>>a;
    cin>>b;
    cin>>c;
    cin>>d;
    set(0,0, a, c);
    int mm=answer();
    output(mm);
}

板凳

/*
算法介绍:
1。读入数据。
2。以菜的个数为主序,采用回溯的方法依次处理每个菜的可能性,返回最好的点菜方法:
      即将问题转化为:从N个不同的数中选出满足要求的M个数。
      解决办法为先从N个不同的数中选出M个数,再判断这M个数是否满足要求,满足要求则存储到数组bestdish[],
并根据当前实际最佳金额与理论最佳金额的差值,实时更换数组bestdish[]的值,最后得到的数组bestdish[]就是
最佳数组bestdish[]。
3。根据数组bestdish[],输出结果。
*/

#include <iostream>
#include<fstream>
#include <time.h>

using namespace std;

const int MAX = 21;
double Min = 1000000;//存储当前实际最佳金额与理论最佳金额的差值
double pay; //存储当前实际最佳金额
double bestPay; //存储所需的理论最佳金额,恰好每人12元
int N, M, K;
int hunCai;
int suCai;
int xinLa;
int qingDan;

class Dish{
public:
      char name[MAX];
      double price;
      bool isMeat;
      bool isAcridity;

      void PutData(const char *na, double p, bool m, bool acr) //输入数据
      {
            strcpy(name, na);
            price = p;
            isMeat = m;
            isAcridity = acr;
      }

      void PrintData()
      {
            cout << name << ' ' << price << ' ' << isMeat << ' ' << isAcridity << endl;
      }
};

void ReadData(const char *filename, Dish **obj);
double Abs(double a);
bool IsPass(const int pass[], const Dish *obj, double & sum);
void GetDishes(int num, int pos, const Dish *obj, int pass[], int bestDish[]);

int main()
{
    time_t startTime;
    time_t endTime;
    time(&startTime);

      Dish *obj;

    ReadData("in3.txt", &obj);//读入数据

 //cout << N << ' ' << M << ' ' << K << endl;
//    for (int i=0; i<N; i++)
//            obj[i].PrintData();
//      cout << hunCai << ' ' << suCai << ' ' << xinLa << ' ' << qingDan << endl;

      bestPay = K * 12; //存储所需的理论最佳金额,恰好每人12元
      int *pass = new int[N+1]; //存储已经被点了的菜
      int *bestDish = new int[N+1]; //存储最佳被点了的菜

      GetDishes(1, 0, obj, pass, bestDish); //以菜的个数用回溯的方法求最佳点菜方案

      for (int i=1; i<=M; i++)
      {
            cout << obj[bestDish[i]].name << endl;
      }
      printf("%.2f\n", pay / K);

      delete []pass;
      delete []bestDish;

    time(&endTime);
//    cout << difftime(endTime, startTime) << endl;

    getchar();
    return 0;
}

void GetDishes(int num, int pos, const Dish *obj, int pass[], int bestDish[])
{
      if (num == M)//处理最后一个菜
      {
            for (int i=pos; i<N; i++)
            {
                  pass[num] = i;
                  double sum = 0;
                  if (IsPass(pass, obj, sum) && Abs(sum-bestPay)<Min) //若该道菜满足口味要求,并最接近理论最佳金额
                  {
                        pay = sum;  //存储当前实际最佳金额
                        Min = Abs(sum-bestPay);//存储当前最小差别
                        for (int i=1; i<=M; i++)
                              bestDish[i] = pass[i];
                  }
            }
      }
      else //如果处理的不是最后一个菜,应采用回溯方法以取得最优解
      {
            for (int i=pos; i<N-M+num; i++)
            {
                 pass[num] = i;
                 GetDishes(num+1, i+1, obj, pass, bestDish);
            }
      }
}

bool IsPass(const int pass[], const Dish *obj, double & sum)//判断该道菜满足口味要求
{
      int h = 0; //存储实际已经点了的荤菜
      int s = 0; //存储实际已经点了的素菜
      int x = 0; //存储实际已经点了的辛辣菜
      int q = 0; //存储实际已经点了的清淡菜
      
      for (int j=1; j<=M; j++)
      {
            sum += obj[pass[j]].price * 0.8;
            if (obj[pass[j]].isMeat && h < hunCai)//分析该道菜的各种属性
                  h++;
            else if (!obj[pass[j]].isMeat && s < suCai)
                  s++;
            else
                  return false;
            if (obj[pass[j]].isAcridity && x < xinLa)
                  x++;
            else if (!obj[pass[j]].isAcridity && q < qingDan)
                  q++;
            else
                  return false;
      }
      return true;
}

double Abs(double a)
{
      return (a > 0) ? a : -a;
}

void ReadData(const char *filename, Dish **obj)
{
      fstream in(filename);
      if (!in)
            return ;   //结束程序执行

      in >> N;
      in >> M;
      in >> K;

      *obj = new Dish[N];
      int n = 0;
      while (!in.eof() && n < N)
      {
            char name[MAX];
            double price;
            bool isMeat;
            bool isAcridity;

            in >> name;
            in >> price;
            in >> isMeat;
            in >> isAcridity;

            (*obj)[n++].PutData(name, price, isMeat, isAcridity);
      }
      in >> hunCai;
      in >> suCai;
      in >> xinLa;
      in >> qingDan;

      in.close(); //关闭文件
}

3 楼

穷举了:
#include <cstdio>
#include <cstdlib>

struct Node 
{
    char name[22];
    int price;
    int hun, la;
};
#define POW(c) (1<<(c))
#define MASK(c) (((unsigned long)-1) / (POW(POW(c)) + 1))
#define ROUND(n, c) (((n) & MASK(c)) + ((n) >> POW(c) & MASK(c)))

int bit_count(unsigned int n)
{
    n = ROUND(n, 0);
    n = ROUND(n, 1);
    n = ROUND(n, 2);
    n = ROUND(n, 3);
    n = ROUND(n, 4);
    return n;
}
int main(int argc, char* argv[])
{
    int n, m, k, i;
    freopen(argv[1], "rt", stdin);
    
    Node menu[16];
    
    scanf("%d%d%d", &n, &m, &k);
    for (i = 0; i < n; i++)
    {
        scanf("%s%d%d%d", menu[i].name, &menu[i].price, &menu[i].hun, &menu[i].la);
    }
    int a, b, c, d, allp = 12 * k, t;
    scanf("%d%d%d%d", &a, &b, &c, &d);
    int besti = -1, bestp = 0;
    for (i = (1<<n) - 1; i >= 0; i--)
    {
        if (bit_count(i) != m)
            continue;
        int aa = 0, bb = 0, cc = 0, dd = 0, p = 0;
        for (t = 0; t < n; t++)
        {
            if (i & (1<<t))
            {
                if (menu[t].hun)
                    aa++;
                else
                    bb++;
                if (menu[t].la)
                    cc++;
                else
                    dd++;
                p += menu[t].price;
            }
        }
        if (aa != a || bb != b || cc != c || dd != d)
            continue;
        
        if (besti == -1)
            besti = i, bestp = p;
        else
        {
            if (abs(p - allp) < abs(bestp - allp))
                besti = i, bestp = p;
        }
    }
    if (besti != -1)
    {
        for (t = 0; t < n; t++)
        {
            if (besti & (1<<t))
            {
                printf("%s\n", menu[t].name);
            }
        }
        printf("%.2f\n", (float)bestp / k);
    }
    
    return 0;
}

4 楼

也做了个回溯式的

#include<iostream>

using namespace std;

typedef struct _CAI
{
    char name[24];
    int  price;
    int  hun;
    int  la;
} _Cai;

static _Cai _cailist[16];
static char want_s[2][4],outlist[16],_getcainum,allcai;
static char curlist[16],markedcai[16];
static int _maxval;

void getall(int cainum,int cur,int uploca,int expect,int curv)
{
    extern void dealitem(int curi,int cainum,int cur,int subv,int expect,int curv);
    if( cur>=cainum ) return;

    for(int i=uploca;i<allcai; ++i)
    {
        char dealflag = 0;
        if( markedcai[i] ) continue;

        int subv,tsum;
        if( ( subv=abs((tsum=_cailist[i].price+curv)-expect) )<_maxval ) dealflag = 1;
        else if( subv==_maxval )
        {
            char newval[4],orgsum,newsum;
            newval[0] = want_s[1][0]+_cailist[i].hun;
            newval[1] = want_s[1][1]+(!_cailist[i].hun);
            newval[2] = want_s[1][2]+_cailist[i].la;
            newval[3] = want_s[1][3]+(!_cailist[i].la);
            orgsum = newsum = 0;
            for(int k=0;k<4; ++k)
                orgsum += abs(want_s[1][k]-want_s[0][k]),
                newsum += abs(newval[k]-want_s[0][k]);
            if( newsum<orgsum ) dealflag = 1;
        }
        else if( (cur+1)<cainum && tsum<expect ) dealflag = 1;

        if( dealflag ) dealitem(i,cainum,cur,subv,expect,curv);

        getall(cainum,cur,i+1,expect,curv);
    }
}

void dealitem(int curi,int cainum,int cur,int subv,int expect,int curv)
{
    markedcai[curi] = 1;
    want_s[1][0] += _cailist[curi].hun;
    want_s[1][1] += (!_cailist[curi].hun);
    want_s[1][2] += _cailist[curi].la;
    want_s[1][3] += (!_cailist[curi].la);
    if( cur+1 == cainum )
    {
        _maxval = subv;
        int kk=0;
        for(int j=0;j<allcai; ++j)
            if( markedcai[j] )
                outlist[kk++] = j;
        _getcainum = kk;
    }
    else getall(cainum,cur+1,curi+1,expect,curv+_cailist[curi].price);
    want_s[1][0] -= _cailist[curi].hun;
    want_s[1][1] -= (!_cailist[curi].hun);
    want_s[1][2] -= _cailist[curi].la;
    want_s[1][3] -= (!_cailist[curi].la);
    markedcai[curi] = 0;
}

int main()
{
    int n,m,k,sum;
    float equa;

    cin >> n >> m >> k;
    if( !(n>0 && n<=16 && m>0 && m<=n && k>0 && k<=12) ) return(-1);
    for(int i=0;i<n; ++i)
        cin >> _cailist[i].name >> _cailist[i].price >> _cailist[i].hun >> _cailist[i].la;
    for(int i=0,j=0;i<4; ++i)
        cin >> j, want_s[0][i] = (char)j, want_s[1][i]=0;
    allcai = n, _getcainum = 0,_maxval=0x7fffffff;
    for(int i=0;i<n; ++i ) markedcai[i] = 0;
    getall(m,0,0,15*k,0);

    sum = 0;
    for(int i=0;i<_getcainum; ++i)
    {
        cout << _cailist[outlist[i]].name << '\n';
        sum += _cailist[outlist[i]].price;
    }

    printf("%.2f",(float)(sum*4)/(float)(5*k));

    return(0);
}

5 楼

两位的都不能从文件中读入数据啊

6 楼

@goal00001111, 5
是读文件的。

7 楼

文件名是什么啊?
题目提供的是in.txt啊

8 楼

[quote]文件名是什么啊?
题目提供的是in.txt啊[/quote]

通过命令行传入,如下:

[quote]freopen(argv[1], "rt", stdin);[/quote]

相关函数说明:

[quote]The  freopen function opens the file whose name is the string pointed to by path and as-
sociates the stream pointed to by stream with it.  The original stream (if it exists) is
closed.   The  mode  argument is used just as in the fopen function.  The primary use of
the freopen function is to change the  file  associated  with  a  standard  text  stream
(stderr, stdin, or stdout).
[/quote]

俺那是不行,我自己就用重定向 '<' 来读文件。

9 楼

这道题数据量小,穷举即可
C(16,8) = 12870

我来回复

您尚未登录,请登录后再回复。点此登录或注册