Allowance POJ - 3040

题意

1 给出n种面值的货币, 并给出数量, 每次去除C元钱, 问最短能取多少回

分析

1 面值比c大的直接当一次
2 面值比C小的贪心求解, 具体分析在注释里

参考代码


struct T{
 int value,num;
 bool operator<(const T&a)
 {
     return value< a.value;
 }
};
T ar[23];
int need[20+6];
int main(void)
{
   int N,C;
   while(cin>>N>>C)
   {
       int sum = 0;
       int number = 0;
      fo1(i,N)
      {
          int a,b;
          cin>>a>>b;
          if(a>=C)//大于C的情况
            sum+=b;
          else
          {
              ar[number].value = a;
              ar[number++].num = b;
          }
      }
     sort(ar,ar+number);//按面值大小排序
// cout<<number<<endl;
    while(number)
     {
         int tmp = C;
         me(need);//初始化need数组,need[i],代表第i中面额取多少
         for(int i = number-1;i>=0;--i)//从大面值到小面值遍历,使得总和小于等于C
         {
            if(ar[i].num)//如果还有剩余
            {
                need[i] = min(ar[i].num,tmp/ar[i].value);//最多能用第i中面值的货币多少被
                tmp -= need[i]*ar[i].value;
            }
// if(tmp==0)
// break;
         }
         for(int i = 0;i < number; ++i)//从小到到大遍历,使得总和大于等于C
         {
             int d = min(ar[i].num-need[i],(tmp+ar[i].value-1)/ar[i].value);//这个是重点, 使得总和大于C<=sum<C+sum[i].value
             need[i] += d;
             tmp -= d*ar[i].value;
         }
         if(tmp>0)//如果剩下的钱不够C
            break;
         else
         {
             int t = INF;//t代表这种组合最多能取多少次
             for(int i = 0;i < number; ++i)
              {
                  if(need[i])
                    t = min(t,ar[i].num/need[i]);
              }
             for(int i = 0;i <number; ++i)
                ar[i].num -= t*need[i];//更新剩余数量
             sum += t;
         }
     }
     cout<<sum<<endl;


   }



    return 0;
}