poj 3040Allowance_可以付多少周工资 (贪心)

来源:互联网 发布:在线室内设计软件 编辑:程序博客网 时间:2024/04/27 20:23

题目点击打开链接

然后

题意

有n种面额的硬币。且面值大的一定能被面值小的整除。现告诉你每种面值硬币的个数。和一个值c。每周支出的钱必须大于等于c。问钱最多能用多少周。

思路:

贪心。大概都能想到。关键是如何寻找最优策略。对于面值大于c的硬币自然不用说。一枚用一周。

对于面值小于c的硬币。我们先考虑一个c的方案。要使用的周数最多我们应该就要使钱的利用率最大。

也就是说损失的钱最少。尽量不要超过c太多。在不超过c的情况下对于大面值和小面值的优先使用大面值的。应为小面值的组合可得到大面值的(整除关系)。留下小面值的给后面的组合最优的可能性越大。如这种策略下没凑够c的话就

找一个最小的面额。使得组合值大于c。(使损失值最小)


#include <iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int INF=0x3f3f3f3f;struct coin{    int v,b;    bool operator <(const coin &s)const    {        return v<s.v;    }}co[25];int need[25];int main(){    int n,m;    cin>>n>>m;    for(int i=0;i<n;i++)    {        cin>>co[i].v>>co[i].b;    }    sort(co,co+n);    int week=0;    int t=-1;    for(int i=n-1;i>=0;i--)    {        if(co[i].v>=m)//对于硬币价值大于m的直接加它的数量 即周数        week+=co[i].b;        else        {           t=i;           break;        }    }    while(1)    {   memset(need,0,sizeof(need));//  别忘记每次重新算时要清零        int rest=m;   //需要硬币数        for(int i=t;i>=0;i--)//  从最大的开始拿        {            if(!rest||co[i].b==0)                continue;              int cod=min(rest/co[i].v,co[i].b);//              need[i]+=cod;//              rest-=cod*co[i].v;//        }        if(rest) // 如果不行那么么就从小的数开始补充        {            for(int i=0;i<=t;i++)            {                if(co[i].v>=rest&&co[i].b-need[i]>0) //如果有 则开始进行补充,补一个即可                {                       need[i]++;                       rest=0;                       break;                }            }            if(rest)// 如果还有剩余 则证明所有可以搞的硬币全部完成,就终止大循环                break;        }        int needd=INF;  //周数        for(int i=0;i<=t;i++)//  去查找每个硬币需要的数目,看看最多当前这种取法可以维持几周            if(need[i])                needd=min(co[i].b/need[i],needd);// 一定要每种所需硬币都有,所以去min               week+=needd;         for(int i=0;i<=t;i++)//  将已选的硬币除去            co[i].b-=need[i]*needd;       }    printf("%d",week);    return 0;}


0 0