poj3040 Allowance (貪心☆☆☆☆☆)
阿新 • • 發佈:2018-12-13
思路來源
題意
你要給某人每週發工資,但是隻有n種固定面額的紙幣。
問在每週不少於其應得工資c的情況下,最多能發多少周。
題解
對於那些大於等於c的工資,顯然每張只能發一次,開頭髮了就好了。
對於那些小於c的工資,先從大到小,看看能不能湊齊c,
期間用used陣列標記每種所需數量,
若不能湊齊,說明肯定要浪費一些,
從小到大,挑浪費面額最少的浪費。
由於之前從大到小遍歷過一遍,
所以浪費的最少的那張不會超過兩張,
故不會出現應該浪費更高面額的值的情況。
這裡有個很好的剪枝,如果某種湊法可以多次使用,
比如湊6元,現有5元100張,1元80張,可以直接cnt+=80,
used相對應減去相應數量即可。
心得
貪心的題思路還是能想到,
然而不看答案自己寫的總是WA,
很迷啊……
程式碼
#include <iostream> #include <algorithm> #include <string> #include <cstring> #include <cstdio> #include <cmath> #include <set> #include <map> #include <vector> #include <stack> #include <queue> #include <bitset> const int INF=0x3f3f3f3f; const int mod=1e9+7; const double eps=1e-7; typedef long long ll; #define vi vector<int> #define si set<int> #define pii pair<int,int> #define pi acos(-1.0) #define pb push_back #define mp make_pair #define lowbit(x) (x&(-x)) #define sci(x) scanf("%d",&(x)) #define scll(x) scanf("%lld",&(x)) #define sclf(x) scanf("%lf",&(x)) #define pri(x) printf("%d",(x)) #define rep(i,j,k) for(int i=j;i<=k;++i) #define per(i,j,k) for(int i=j;i>=k;--i) #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; ll n,c,ans,used[25]; bool flag; struct RMB { ll v,num; }; bool cmp(RMB a,RMB b) { return a.v<b.v; } RMB q[25]; int main() { while(~scanf("%lld%lld",&n,&c)) { mem(q,0);mem(used,0);flag=0;ans=0; rep(i,0,n-1)scll(q[i].v),scll(q[i].num); sort(q,q+n,cmp); per(i,n-1,0)//先挑超過c的直接給 { if(q[i].v>=c)ans+=q[i].num,q[i].num=0; else break; } while(1) { mem(used,0);flag=0; ll tempc=c; for(int i=n-1;i>=0&&!flag;--i) { if(q[i].num) { ll num=min(q[i].num,tempc/q[i].v); //q[i].num-=num;//注意 此時還沒有真用 tempc-=num*q[i].v; used[i]=num;//只是借用 if(!tempc)flag=1;//本方案正好能湊齊c } } if(tempc>0) { for(int i=0;i<n&&!flag;++i) { if(q[i].num>used[i])//從小到大浪費 { while(used[i]<q[i].num&&!flag) { tempc-=q[i].v; used[i]++; if(tempc<0)flag=1; } } } } if(!flag)break; ll cnt=0x7f7f7f7f;//cnt確定該發放工資方案能用幾次 rep(i,0,n-1)if(used[i])cnt=min(cnt,q[i].num/used[i]); ans+=cnt; rep(i,0,n-1)q[i].num-=used[i]*cnt; } printf("%lld\n",ans); } return 0; }