1. 程式人生 > >poj3040 Allowance (貪心☆☆☆☆☆)

poj3040 Allowance (貪心☆☆☆☆☆)

思路來源

題意

你要給某人每週發工資,但是隻有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;
}