poj之旅——3040

题目描述:农夫约翰要给奶牛Bessie发工资了,每周至少 C 元。约翰手头上有面值V_i的硬币B_i个,求最多能发几周?



题解:

贪心策略是使多发的面额最小(最优解)。分三个阶段:

  1. 首先面额不小于C的硬币属于没办法节约的类型,先统统发掉。

  2. 然后对硬币面额从大到小尽量凑得接近C,允许等于或不足C,但是不能超出C。

  3. 接着按硬币面额从小到大凑满C(凑满的意思是允许超出一个最小面值,ps此处的最小面值指的是硬币剩余量不为0的那些硬币中的最小面值),凑满之后得出了最优解,发掉,进入步骤2.

  4. 这样就保证了每次都是当前的最优解,这个题很好地体现了贪心法的精髓。


参考程序:

#include<cstdio>
#include<algorithm>
#include <functional>
#include <limits>
#include<string>
#include<iostream>
using namespace std;
struct Node{
	int v,c;
}end;
Node coin[20];
int need[20];
bool cmp(Node a,Node b){
	return (a.v>b.v);
}
int main(){
	int N,C,week=0;
	scanf("%d %d",&N,&C);
	for (int i=0;i<N;i++)scanf("%d %d",&coin[i].v,&coin[i].c);
	for (int i=0;i<N;i++)if (coin[i].v>=C)week+=coin[i].c,coin[i].c=0;
	sort(coin,coin+N,cmp);
	while (true){
		int sum=C;
		memset(need,0,sizeof(need));
		for (int i = 0; i < N; ++i)
			if (sum > 0 && coin[i].c > 0){
				int can_use = min(coin[i].c, sum / coin[i].v);
				if (can_use > 0){
					sum -= can_use * coin[i].v;
					need[i] = can_use;
				}
			}
		for (int i = N - 1; i >= 0; --i)
			if (sum > 0 && coin[i].c > 0){
				int can_use = min(coin[i].c - need[i],(sum + coin[i].v - 1) / coin[i].v); 
				if (can_use > 0){
					sum -= can_use * coin[i].v;
					need[i] += can_use;
				}
			}
		if(sum > 0)break; 
		int add_up = numeric_limits<int>::max(); 
		for (int i = 0; i < N; ++i){
			if (need[i] == 0)continue;
			add_up = min(add_up, coin[i].c / need[i]);
		}
		week += add_up;
		for (int i = 0; i < N; ++i){
			if (need[i] == 0)continue;
			coin[i].c -= add_up * need[i];
		}
   }
   printf("%d",week);
	return 0;
}


你可能感兴趣的:(poj之旅——3040)