贪心算法 -- gone fishing

poj 1042 gone fishing

题目要求:

   由有n个湖, 按照顺序排列,一个人从第一个湖向最后一个湖行进(方向只能从湖0到湖n-1),途中可以在湖中钓鱼。在每个湖中钓鱼时,开始的5分钟内可以钓到 f[i] 条,之后每5分钟钓到的鱼数目递减 d[i] ,且每两个相邻的湖的距离 t[i] 给出(t[i] 表示 由第 i 个湖向第 i + 1个湖行进在路上花费5分钟的倍数的时间, 即如果t[3] = 4,则表示从第三个湖向第四个湖行进需要花费20分钟)。 现给定总时间h 小时,求出在每个湖钓鱼时间的最好的方案,使得钓鱼的总数最多,如果两种方案结果相同,则输出在较小序号的湖中钓鱼时间更多的那个。

题目分析:

  最优化问题,可以选择动态规划或者贪心算法,初看起来,是个多阶段的决策问题,且每个阶段会对下一个阶段产生影响,并不好找到合适的贪心策略,更适合采用动态规划。先考虑动态规划算法,可以考虑 动规数组 dp[t][n] 表示在 t 时刻到达湖 n 时之前钓鱼结果的最优值,可以找出递推关系式 dp[t][n] =  max{ dp[t1][n-1] + fish_result(n-1, t - t1 - t[n-1])}

即对到达湖 n - 1的时刻 t1 进行枚举(在一定的范围之内,小于 t - t[n-1], 大于xx),然后取出最大值作为 dp[t][n],这样从 n - 1 推到 n。

  个人认为这种动态规划的方法是可行的,但是。。由于水平太菜。。没有成功。。

  考虑采用贪心的算法来实现,仔细分析一下,当确定了需要到达的湖的个数n,也就确定了在路上需要消耗的时间,从而得到钓鱼的总时间,这样在规定的时间内钓鱼,每次都选择当前5分钟内收益最大的那个湖进行钓鱼(可以采用优先级队列  priority_queue 来实现)。 考虑到这样做会有可能出现先在湖 A 中钓鱼,再在湖B中钓鱼,然后再回到湖A中钓鱼... 但是这样不会影响最终的结果(这也是贪心算法可以运用到此题的前提)。因为,虽然只能从湖0到湖n-1行进,此时进行贪心选择虽然每次选择的顺序可能不断颠倒变化,但是这只是确定湖A有几次被选中钓鱼,也就是确定从湖0到湖n-1每个湖中钓鱼的时间。在实际行进途中钓鱼的时候,就按照贪心算出来的方案(在湖 i 中钓鱼几次),在途中的湖中钓鱼。

  通过固定要经过的湖的个数n,将每个湖由于在路上消耗时间的差异去掉,使得面临的选择无差异,可以运用贪心算法。

实现代码:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
using namespace std;
#define MAX_INTERVAL_NUM 200
#define MAX_LAKE_NUM 25

int dist_time[MAX_LAKE_NUM];
int min_dist_time[MAX_LAKE_NUM];
int stay_times[MAX_LAKE_NUM];
int init_num[MAX_LAKE_NUM];
int dec_num[MAX_LAKE_NUM];

struct LakeNode{
    int lake_index;
    int cur_fish_amount;
    int dec_amount;
    int stay_time;
};
LakeNode lake_nodes[MAX_LAKE_NUM];
struct cmp{
    bool operator() (LakeNode* lake1, LakeNode* lake2){
        if (lake1->cur_fish_amount == lake2->cur_fish_amount){
            return lake1->lake_index > lake2->lake_index;
        }
        return lake1->cur_fish_amount < lake2->cur_fish_amount;
    }
};

void ClearQueue(priority_queue<LakeNode*, vector<LakeNode*>, cmp>& queue){
    while(! queue.empty()){
        queue.pop();
    }
}

int Resolve(int N, int T){
    priority_queue<LakeNode*, vector<LakeNode*>, cmp> lake_queue;
    LakeNode* lake;
    int result = 0, max_amount = 0;
    stay_times[0] = T;
    for (int n = 1; n <= N; n ++){
        result = 0;
        int fish_time = T - min_dist_time[n - 1];
        if (fish_time <= 0){
            break;
        }
        ClearQueue(lake_queue);
        for(int i = 0; i < n; i ++){
            lake_nodes[i].stay_time = 0;
            lake_nodes[i].lake_index = i;
            lake_nodes[i].cur_fish_amount = init_num[i];
            lake_nodes[i].dec_amount = dec_num[i];

            lake_queue.push((LakeNode*)&lake_nodes[i]);
        }
        while(! lake_queue.empty() && fish_time > 0){
            lake = lake_queue.top();
            lake_queue.pop();

            if (lake->cur_fish_amount > 0){
                result += lake->cur_fish_amount;
                lake->cur_fish_amount -= lake->dec_amount;
            }
            if (lake->cur_fish_amount <= 0){
                lake->cur_fish_amount = 0;
            }

            lake->stay_time ++;
            fish_time --;
            lake_queue.push(lake);

        }
        if (result > max_amount){
            max_amount = result;
            for(int i = 0; i < n; i ++){
                stay_times[i] = lake_nodes[i].stay_time;
            }
        }
    }
    return max_amount;
}

int main(){
    int n, h;

    while(true){
        cin >> n;
        if (n == 0){
            break;
        }
        cin >> h;
        h = h*12;
        for(int i = 0; i < n ; i ++){
            cin >> init_num[i];
            stay_times[i] = 0;
        }
        for(int i = 0; i < n; i ++){
            cin >> dec_num[i];
        }
        min_dist_time[0] = 0;
        cin >> dist_time[0];
        for(int i = 1; i < n - 1; i ++){
            cin >> dist_time[i];
            min_dist_time[i] = min_dist_time[i - 1] + dist_time[i-1];
        }
        min_dist_time[n-1] = min_dist_time[n-2] + dist_time[n-2];

        int result = Resolve(n, h);
        cout << stay_times[0]*5 ;
        for(int i = 1; i < n; i ++){
            cout << ", " << stay_times[i]*5;
        }
        cout << endl << "Number of fish expected: " << result << endl << endl;
    }
    return 0;
}
时间: 2024-08-24 03:42:03

贪心算法 -- gone fishing的相关文章

算法专题——贪心算法

贪心算法正确性证明: 1.证明贪心选择性质:经过贪心选择,可以获得最优解 2.最优子结构:证明子问题最优解与贪心选择组合,结果依然是最优解 •All we really need to do is argue that an optimal solution to the subproblem, combined with the greedy choice already made, yields an optimal solution to the original problem. 例:

POJ1017 Packets(贪心算法训练)

Time Limit: 1000MS          Memory Limit: 10000K          Total Submissions: 51306          Accepted: 17391 Description A factory produces products packed in square packets of the same height h and of the sizes 1*1, 2*2, 3*3, 4*4, 5*5, 6*6. These pro

贪心算法的简述与示例

贪心算法采用自顶向下,以迭代的方法做出相继的贪心选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题,通过每一步贪心选择,可得到问题的一个最优解,虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪婪法不要回溯.能够用贪心算法求解的问题一般具有两个重要特性:贪心选择性质和最优子结构性质. 参考:http://babybandf.blog.163.com/blog/static/61993532010112923767/ [例1]删数问题[B][/B] 试题描

算法导论——lec 13 贪心算法与图上算法

之前我们介绍了用动态规划的方法来解决一些最优化的问题.但对于有些最优化问题来说,用动态规划就是"高射炮打蚊子",采用一些更加简单有效的方法就可以解决.贪心算法就是其中之一.贪心算法是使所做的选择看起来是当前最佳的,期望通过所做的局部最优选择来产生一个全局最优解. 一. 活动选择问题 [问题]对几个互相竞争的活动进行调度:活动集合S = {a1, a2, ..., an},它们都要求以独占的方式使用某一公共资源(如教室),每个活动ai有一个开始时间si和结束时间fi ,且0 ≤ si &

五大常用算法之三贪心算法

贪心算法 贪心算法简介: 贪心算法是指:在每一步求解的步骤中,它要求"贪婪"的选择最佳操作,并希望通过一系列的最优选择,能够产生一个问题的(全局的)最优解. 贪心算法每一步必须满足一下条件: 1.可行的:即它必须满足问题的约束. 2.局部最优:他是当前步骤中所有可行选择中最佳的局部选择. 3.不可取消:即选择一旦做出,在算法的后面步骤就不可改变了. 贪心算法案例: 1.活动选择问题  这是<算法导论>上的例子,也是一个非常经典的问题.有n个需要在同一天使用同一个教室的活动a

零基础学贪心算法

本文在写作过程中参考了大量资料,不能一一列举,还请见谅.贪心算法的定义:贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择.也就是说,不从整体最优上加以考虑,只做出在某种意义上的局部最优解.贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关.解题的一般步骤是:1.建立数学模型来描述问题:2.把求解的问题分成若干个子问题:3.对每一子问题求解,得到子问题的局部最优解:4.把子问题的局部最优

贪心算法

一,贪心算法的设计思想 ? 从问题的某一个初始解出发逐步逼近给定的目标,每一步都作一个不可回溯的决策,尽可能地求得最好的解.当达到某算法中的某一步不需要再继续前进时,算法停止. 二,贪心算法的基本性质 1)贪心选择性质 所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到.这是贪心算法可行的第一个基本要素,也是贪心法与动态规划法的主要区别. 2) 最优子结构性质 该问题解的整体最优性依赖于其局部子问题解的最优性.这种性质是可以采用贪心算法解决问题的关键特征.例如

算法导论----贪心算法,删除k个数,使剩下的数字最小

先贴问题: 1个n位正整数a,删去其中的k位,得到一个新的正整数b,设计一个贪心算法,对给定的a和k得到最小的b: 一.我的想法:先看例子:a=5476579228:去掉4位,则位数n=10,k=4,要求的最小数字b是n-k=6位的: 1.先找最高位的数,因为是6位数字,所以最高位不可能在后5位上取到(因为数字的相对顺序是不能改变的,假设如果取了后五位中倒数第5位的7,则所求的b就不可能是6位的了,最多也就是4位的79228)理解这点很重要!所以问题变成从第1位到第k+1(n-(n-k-1))取

高级算法——贪心算法(找零问题)

function makeChange(origAmt, coins) {//贪心算法——找零问题 var remainAmt ; if (origAmt % .25 < origAmt) { coins[3] = parseInt(origAmt / .25); remainAmt = origAmt % .25; origAmt = remainAmt; } if (origAmt % .1 < origAmt) { coins[2] = parseInt(origAmt / .1); r