贪心法 习题10道

HDOJ 1009 FatMouse’ Trade

思路

按照J[i]/F[i]排序。从比值大的开始计算。

#include<bits/stdc++.h>
using namespace std;
struct food{
    
    
    int j;
    int f;
    double rate;
}getfood[1001];
bool comp(struct food a,struct food b){
    
    
    return (a.rate) < (b.rate);
}
int main(){
    
    
    int m,n;
    while(~scanf("%d %d",&m,&n) && m!=(-1) && n!=(-1)){
    
    
        for(int i=0;i<n;i++){
    
    
            scanf("%d %d",&getfood[i].j,&getfood[i].f);
            getfood[i].rate = (double)getfood[i].j/getfood[i].f;
        }
        sort(getfood,getfood+n,comp);
        double sum = 0;
        while(n && n>0){
    
    
            n--;
            if(m>=getfood[n].f) {
    
    sum += getfood[n].j;m -= getfood[n].f;}
            else {
    
    sum += getfood[n].rate * m;break;}
        }
        printf("%.3f\n",sum);
    }
    return 0;
}

HDOJ 1049 Climbing Worm

思路

每一次向上爬完之后判断有没有到达顶部,如果到达就不再下滑。

#include<bits/stdc++.h>
int main(){
    
    
    int n,u,d,high,time;
    while(~scanf("%d %d %d",&n,&u,&d) && n ){
    
    
        high=0;
        time=0;
        while(1){
    
    
            if(high+u>=n){
    
    
                time++;
                printf("%d\n",time);
                break;
            }else{
    
    
                time += 2;
                high += u-d;
            }
        }
    }
    return 0;
}

HDOJ 1050 Moving Tables

思路

这是一个巧妙的想法。需要注意 s 和 t 的大小之分。

#include<bits/stdc++.h>
using namespace std;
int main(){
    
    
    int ti;
    scanf("%d",&ti);
    while(ti--){
    
    
        int n,s,t,num[201]={
    
    0};
        scanf("%d",&n);
        for(int i=0;i<n;i++){
    
    
            scanf("%d %d",&s,&t);
            if(s>t) swap(s,t);
            for(int j=(s+1)/2;j<=(t+1)/2;j++) num[j]+=1;
        }
        sort(num, num+201, greater<int>());
        printf("%d\n",num[0]*10);
    }
    return 0;
}

HDOJ 1051 Wooden Sticks

思路

递增排序。
在每一个设置时间内,之后符合的木头都不能比前一个符合的木头大,而不是比设置时间的第一个木头大。

#include<bits/stdc++.h>
using namespace std;
struct move{
    
    
    int l;
    int w;
    int sign;
};
bool comp(struct move a, struct move b){
    
    
    if(a.l==b.l) return a.w<b.w;
    return a.l<b.l;
}
int main(){
    
    
    int t;
    scanf("%d",&t);
    while(t--){
    
    
        int n=0;
        scanf("%d",&n);
        struct move num[n+2] = {
    
    {
    
    0,0,0}};
        for(int i=2;i<=n+1;i++){
    
    
            scanf("%d %d",&num[i].l,&num[i].w);
        }
        sort(num+2, num+n+2, comp);

        int second=0,all=n,start=2;
        num[1]=num[2];
        while(all){
    
    
            num[0] = num[1];
            second++;
            int change=0;
            for(int i=start;i<=n+1;i++){
    
    
                if(num[i].w>=num[0].w && num[i].sign==0){
    
    
                    all--;
                    num[0] = num[i];
                    num[i].sign=1;
                }
                else if(change==0 && num[i].sign==0){
    
    num[1]=num[i];change=1;start=i;}
            }
        }
        printf("%d\n",second);
    }
    return 0;
}

HDOJ 1052 Tian Ji – The Horse Racing

思路

评论区的数据
田忌赛马的故事。
将两人的马速 降序 排列。分别用两个头下标,两个尾下标。额外的变量计算赢和输的次数。


1.如果 田忌 最快的马 比 国王 最快的马快,直接比,赢掉,两个头下标加一。
2.如果 田忌 最快的马 比 国王 最快的马慢,不能直接比,用田忌最慢的马 去和 国王最快的马比赛,输掉。田忌尾下标减一,国王头下标加一。
3.如果两人最快的马的速度相同,先不打平局;去看最慢的马:

  1. 如果田忌最慢的马 比 国王最慢的马快,直接比,赢掉
  2. 如果田忌最慢的马 比 国王最慢的马慢,这个最慢的马肯定要输,就去和国王最快的马去比赛,输掉
  3. 如果两人最慢的马的速度也相同,就用田忌最慢的马 和 国王最快的比,此时需要注意:
    <1>如果田忌最慢的马 等于 国王最快的马:平局
    <2>如果田忌最慢的马 比 国王最快的马慢,直接比,输掉
    <3>不存在田忌最慢的马 比 国王最快的马快

循环判断条件:田忌的头下标<=田忌的尾下标。(因为最后一次赛马,头和尾下标一定是相同的)

#include<bits/stdc++.h>
using namespace std;
int main(){
    
    
    int n=1;
    while(~scanf("%d",&n) && n){
    
    
        int tian[n]={
    
    0};
        int king[n]={
    
    0};
        for(int i=0;i<n;i++) scanf("%d",&tian[i]);
        for(int i=0;i<n;i++) scanf("%d",&king[i]);
        sort(tian,tian+n ,greater<int>() ); //递减排序
        sort(king,king+n ,greater<int>() );

        int t=0,k=0;             //从头开始的两个头下标
        int last1=n-1,last2=n-1; //从尾开始的两个尾下标
        int win=0,lose=0;        //平局不计算在内
        while( t <= last1 ){
    
         //当tian的头下标大于tian的尾下标时结束。因为存在相等的时候但是赛马还没结束
            if( tian[t] > king[k]){
    
    
                win++;
                t++;
                k++;
            }else if ( tian[t] < king[k] ){
    
    
                k++;
                lose++;
                last1--;
            }else{
    
    
                if (tian[last1] > king[last2]){
    
    
                    win++;
                    last1--;
                    last2--;
                }else if (tian[last1] < king[last2]){
    
    
                    last1--;
                    k++;
                    lose++;
                }else{
    
    
                    if (tian[last1] == king[k]){
    
    
                        last1--;
                        k++;
                    }else{
    
    
                        last1--;
                        k++;
                        lose++;
                    }
                }
            }
        }
        printf("%d\n",(win-lose)*200);
    }
    return 0;
}

HDOJ 1257 最少拦截系统

思路

四种方法

HDOJ 1789 Doing Homework again

思路

活动安排问题。
问题简介:一个作业做一天,做不完作业会减少分数。求做作业的顺序使得减少的分数最少。
步骤:
1按照分数递减排序
2从头开始遍历,将作业放在截止日期那一天完成:
->如果那一天被占用,就往前推直到有空闲位置
->如果没有位置,就拒绝作业,进行扣分

#include <bits/stdc++.h>
using namespace std;
struct work{
    
    
    int time;
    int score;
};
bool comp(struct work a, struct work b){
    
    
    if(a.score==b.score) return a.time > b.time;
    return a.score>b.score;
}
int main(){
    
    
    int T;
    scanf("%d",&T);
    while(T--){
    
    
        int n,maxday=0;
        scanf("%d",&n);
        struct work home[n];
        for(int i=0;i<n;i++) {
    
    scanf("%d",&home[i].time);maxday = maxday<home[i].time?home[i].time:maxday;}
        for(int i=0;i<n;i++) scanf("%d",&home[i].score);
        sort(home,home+n,comp);

        int reduce=0,day=0;
        int use[ maxday+1]={
    
    0};
        for(int i=0;i<n;i++){
    
    
            day = home[i].time;
            while (use[day]==1 && day>=1) day--;
            if(day>=1) use[day]=1;
            else reduce += home[i].score;
        }
        printf("%d\n",reduce);
    }
    return 0;
}

HDOJ 2546 饭卡

思路

普通背包问题。
步骤:
0判断余额是否小于5,如果小于直接输出余额;
1把价格最高的菜选出来但是不买;
2计算m-5最多能买到多少菜 (0/1背包问题);
3买完后,m-购买菜-价格最高菜。

#include <bits/stdc++.h>
using namespace std;

int price[1010],m,n;
int dp[1010]; // 0/1背包,使用滚动一维数组,第二层循环是反过来循环
int solve(int m){
    
    
    memset(dp,0,sizeof(dp));
    for(int i=1;i<n;i++){
    
     //n-1种菜,最后一种是价格最高的菜,不计算在内
        for(int j=m;j>=price[i];j--){
    
     //m钱,也就是空间大小是m
            dp[j] = max( dp[j], dp[j-price[i]]+price[i]);
        }
    }
    return dp[m];
}

int main(){
    
    
    while(~scanf("%d",&n) && n){
    
    
        memset(price,0,sizeof(price));
        for(int i=1;i<=n;i++) scanf("%d",&price[i]);
        scanf("%d",&m);
        if(m<5){
    
    
            printf("%d\n",m);
            continue;
        }
        sort(price+1,price+n+1,less<int>() );
        printf("%d\n",m-solve(m-5)-price[n]);
    }
    return 0;
}

POJ 1089 Intervals

思路

参考贪心法常见问题:区间覆盖问题
问题简介:给定很多线段,合并线段,判断一共能合并成几个线段并输出。
步骤:
0.设定区间 [ start, end ] 为当前经过区间合并后形成的区间。
1.按照 左端点递增排序 ,初始化 [ start, end ] 为第一个区间
2.遍历之后的区间,判断当前区间的左端点是否小于等于 end:
–>如果小于,那么表示这个区间可以合并在 [ start, end ] 中;然后判断是否可以更新 end 为这个区间的右端点;
–>否则,表示这个区间和 [ start, end ] 不可能合并。输出 [ start, end ] 。重置 [ start, end ] 为当前区间;继续遍历
3.遍历结束,输出 [ start, end ] ,即最后一个合并好的区间。

#include<iostream>
#include<algorithm>
using namespace std;
struct interval{
    
    
    int left;
    int right;
}intervals[50100];
bool comp(struct interval a, struct interval b){
    
    
    if(a.left==b.left) return a.right < b.right;
    return a.left<b.left;
}
int main(){
    
    
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%d %d",&intervals[i].left,&intervals[i].right);
    sort(intervals,intervals+n,comp);
    int start=intervals[0].left, end=intervals[0].right;
    for(int i=1;i<n;i++){
    
    
        if(intervals[i].left<=end) end = intervals[i].right>end?intervals[i].right:end;
        else {
    
    
            printf("%d %d\n",start,end);
            start = intervals[i].left;
            end = intervals[i].right;
        }
    }
    printf("%d %d\n",start,end);
    return 0;
}

POJ 1328 Radar Installation

思路

参考贪心法常见问题:活动安排问题
几何问题,建模为活动安排问题。


步骤:
1.以每个点为圆心以d为半径画圆,与x轴的两个交点,作为一个线段。这样做,表示,只要雷达在这个线段范围内,就可以辐射到这个岛屿。得到n个线段
2.(开始贪心法常见问题:活动安排问题)把这些线段,按照右端点递增排序左端点不用管。
3.设置一个 [start, end],表示在我们的算法中,这个区域需要存放一个雷达。初始值为第一条线段的两端。雷达数量设置为1
4.从第二条线段开始遍历:
–>如果当前线段右端点 大于 end,表示,[start, end]区域中的雷达不能辐射到当前岛屿。雷达数量加一,并更新 end=当前线段右端点。也就是说,现在新成立了一个雷达。
–>(不需要考虑当前线段右端点 小于 end的情况,因为这种情况就是当前雷达可以辐射到当前岛屿,什么都不用做,考虑下一个岛屿即可。注意,我们最开始的时候,是按照线段右端点递增排序的。)
5.结束,输出雷达数量。


重点!!!我WA了好几次好几次,发现了问题之处。当然不是算法的问题。
这道题我们需要考虑边缘数据。
半径d<0 ;;每一个点的纵坐标y<0 ;;每一个点的纵坐标y>d
这三种情况下,输出 -1
但是!!!输出 -1 必须要在所有 scanf 之后才能判断进而输出 -1,不能在开始就判断。不然的话你提前输出了 -1,这一组测试数据还没有获取完,就会WA!!!

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
struct point {
    
                                  //存储每一个点的横纵坐标,然后变成每一个线段
    double x;
    double y;
}islands[1010];;
bool comp(struct point a, struct point b){
    
      //排序用到的自定义函数
    return a.y<b.y;
}
int main() {
    
    
    int n,d,time=0,sign;
    while (scanf("%d %d",&n,&d) && (n && d)){
    
    
        time++;
        sign=0;
        for(int i=0;i<n;i++){
    
                   //获取输入
            scanf("%lf %lf",&islands[i].x, &islands[i].y);
            if(islands[i].y<0 || islands[i].y>d) sign=1;
            islands[i].x = islands[i].x - pow( d*d-pow(islands[i].y,2), 0.5);
            islands[i].y = islands[i].x + 2*pow( d*d-pow(islands[i].y,2), 0.5);
        }
        if(d<0){
    
    
            printf("Case %d: -1\n",time);       //边缘数据判断
            continue;
        }
        if(sign){
    
                                   //边缘数据判断
            printf("Case %d: -1\n",time);
            continue;
        }
        sort(islands,islands+n,comp);       //按照右端点递增排序

        int num=1;
        double end = islands[0].y;
        for(int i=1;i<n;i++)            //算法核心
            if(islands[i].x > end){
    
    
                num ++;
                end = islands[i].y;
            }
        printf("Case %d: %d\n",time,num);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_46523755/article/details/115094797
今日推荐