zoukankan      html  css  js  c++  java
  • 【贪心算法】POJ-3040 局部最优到全局最优

    一、题目

    Description

    As a reward for record milk production, Farmer John has decided to start paying Bessie the cow a small weekly allowance. FJ has a set of coins in N (1 <= N <= 20) different denominations, where each denomination of coin evenly divides the next-larger denomination (e.g., 1 cent coins, 5 cent coins, 10 cent coins, and 50 cent coins).Using the given set of coins, he would like to pay Bessie at least some given amount of money C (1 <= C <= 100,000,000) every week.Please help him ompute the maximum number of weeks he can pay Bessie.

    Input

    • Line 1: Two space-separated integers: N and C

    • Lines 2..N+1: Each line corresponds to a denomination of coin and contains two integers: the value V (1 <= V <= 100,000,000) of the denomination, and the number of coins B (1 <= B <= 1,000,000) of this denomation in Farmer John's possession.
      Output

    • Line 1: A single integer that is the number of weeks Farmer John can pay Bessie at least C allowance

    Sample Input

    3 6
    10 1
    1 100
    5 120

    Sample Output

    111

    Hint

    INPUT DETAILS:
    FJ would like to pay Bessie 6 cents per week. He has 100 1-cent coins,120 5-cent coins, and 1 10-cent coin.

    OUTPUT DETAILS:
    FJ can overpay Bessie with the one 10-cent coin for 1 week, then pay Bessie two 5-cent coins for 10 weeks and then pay Bessie one 1-cent coin and one 5-cent coin for 100 weeks.

    二、思路&心得

    • 贪心题目:从局部最优解得到全局最优解。

    • 贪心策略如下:先对数据按照金额从小到大进行排序。对于金额大于C的纸币,直接全部取出;之后进行若干次循环,每次循环中先从大到小尽可能取到小于C的最大金额,之后再从小到大尽可能凑满C,允许超出一个当前最小金额值,一次处理结束后更新相应金额的数量。

    • 这个贪心策略的数学化证明暂时没有想到,题目中还给出“金额之间还有确定的倍数关系”,也不清楚这个信息在算法中具体体现了什么作用。对于这个贪心策略,一个较为直观的解释如下:类比生活中买东西,当消费了一定金额后,我们肯定都是使用尽可能多的大面值的RMB,再使用小面值的,整个题目的思想应该跟这个差不多,我们生活中都下意识使用了很多的贪心策略。

    • PS:做这个题目时,实在被坑了好久,花了三四个小时,RE + WA无数次才过,主要的问题还是自己一开始上手时,所使用的贪心策略完全错误,尝试了多种,并且举反例证明之后才逐渐找到了正确的贪心策略。

    三、代码

    #include<cstdio>
    #include<climits>
    #include<algorithm>
    using namespace std;
    const int MAX_N = 25;
    
    int N, C;
    
    int ans;
    
    int use[MAX_N];
    
    struct Money {
    	int value;
    	int number;
    } a[MAX_N];
    
    bool cmp(Money a, Money b) {
    	return a.value < b.value;
    }
    
    void solve() {
    	int i, start = -1;
    	int min_num, dist;
    	//计算所有面额大于C的数据 
    	for (i = N - 1; i >= 0; i --) {
    		if (a[i].value >= C) {
    			ans += a[i].number;
    		} else {
    			start = i;
    			break;
    		}
    	}
     	while (1) {
    		fill(use, use + N, 0);
    		dist = C;
    		//从大到小取到小于C的最大数值 
    		for (i = start; i >= 0; i --) {
    			if (a[i].number) {
    				min_num = min(dist / a[i].value, a[i].number);
    				use[i] = min_num;
    				dist -= a[i].value * min_num;
    			}
    		}
    		//从小到大凑满C 
    		if (dist > 0) {
    			for (int i = 0; i <= start; i ++) {
    				if (a[i].number) {
    					min_num = min((dist + a[i].value - 1) / a[i].value, a[i].number - use[i]);
    					use[i] += min_num;
    					dist -= a[i].value * min_num;
    					if (dist <= 0) break;
    				}
    			}
    		}
    		if (dist > 0) break;
    		//数量更新 
    		min_num = INT_MAX;
    		for (i = 0; i <= start; i ++) {
    			if (use[i])
    				min_num = min(min_num, a[i].number / use[i]);
    		}
    		for (i = 0; i <= start; i ++) {
    			if (use[i]) {
    				a[i].number -= min_num * use[i];
    			}
    		}
    		ans += min_num;
    	}
    	printf("%d
    ", ans);
    }
    
    int main() {
    	while (~scanf("%d %d", &N, &C)) {
    		ans = 0;
    		for (int i = 0; i < N; i ++) {
    			scanf("%d %d", &a[i].value, &a[i].number);
    		}
    		sort(a, a + N, cmp);		
    		solve();
    	}
    	return 0;
    }
    
  • 相关阅读:
    if控制器+循环控制器+计数器,控制接口分支
    前置处理器
    逻辑控制器
    配置元件
    基础元件
    docker etcdctl报错:etcdctl No help topic for 'put'
    celery定时执行ansible api返回为空的问题
    Ansible+Jenkins+Gitlab搭建及配置
    进击的Python【第十六章】:Web前端基础之jQuery
    进击的Python【第十五章】:Web前端基础之DOM
  • 原文地址:https://www.cnblogs.com/CSLaker/p/7307771.html
Copyright © 2011-2022 走看看