贪心法讲解

 

例题


 

[ 一 ] 旅行家的预算

题目:

https://vijos.org/p/1253

  具体思路如下:

    1.如果不能到达,输出 -1

    2.如果之后的序列中存在小于等于(注意取等号),找到第一个(证明:如果不是第一个最小值……),计算,加油到刚好到达目标点。

    3.如果没有更小的,找到能到达的最小值,计算,加满油且没有越过终点

  代码见下:

  

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
struct Sto {
	double Pri;
	double Dist;
} Pot[9];
double Abt,v,Dist;
int n;
int main() {
	scanf("%lf%lf%lf%lf%d",&Dist,&v,&Abt,&Pot[0].Pri,&n);
	for(int i = 1; i<=n; i++) {
		scanf("%lf%lf",&Pot[i].Dist,&Pot[i].Pri);
	}
	double Cost = 0,Now;
	int End = 0;
	for(int i = 0; i<n; i++) {
		if(Pot[i].Pri > Pot[i + 1].Pri) {
			Cost += (Pot[i + 1].Dist - Now * Abt - Pot[i].Dist) * Pot[i].Pri / Abt;
			End = i + 1;
			Now = 0;
		} else {
			double Max = v * Abt;
			if(Max < Pot[i + 1].Dist - Pot[End].Dist) {
				printf("No Solution");
				return 0;
			} else {
				double Min = 1000;
				int Idx = 0;
				for(int j = i + 1; j<=n; j++) {
					if(Pot[j].Dist - Pot[End].Dist > Max) {
						break;
					}
					if(Pot[j].Pri < Min) {
						Min = Pot[j].Pri;
						Idx = j;
						if(Min < Pot[End].Pri) {
							break;
						}
					}
				}
				if(Min > Pot[End].Pri) {
					Cost += (v - Now) * Pot[End].Pri;
					Now = v - (Pot[Idx].Dist - Pot[End].Dist) / Abt;
					if(Now * Abt >= Dist - Pot[Idx].Dist) {
						Cost -= (Now * Abt - Dist + Pot[Idx].Dist) * Pot[End].Pri / Abt;
						printf("%.2lf",Cost);
						return 0;
					}
				} else {
					Cost += (Pot[Idx].Dist - Now * Abt - Pot[End].Dist) * Pot[End].Pri / Abt;
					Now = 0;
				}
				i = Idx - 1;
				End = Idx;
			}
		}
	}
	Cost += (Dist - Pot[End].Dist - Now * Abt) * Pot[End].Pri / Abt;
	printf("%.2lf",Cost);
	return 0;
}

 

 [ 二 ] 均分纸牌

题目:

https://www.luogu.org/problemnew/show/P1031

  具体思路:

    1.先确定每堆牌应有的数量

    2.将每堆牌数于平均值相减

    3.做出来了

  

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int a[105];
int main() {
	int n,Tot = 0;
	scanf("%d",&n);
	for(int i = 1; i<=n; i++) {
		scanf("%d",&a[i]);
		Tot += a[i];
	}
	Tot /= n;
	int Ans = 0;
	for(int i = 1; i<=n; i++) a[i] -= Tot;
	for(int i = 1; i<=n; i++) {
		if(a[i]) {
			a[i + 1] += a[i];
			Ans ++;
		}
	}
	printf("%d",Ans);
	return 0;
}

 

  [ 三 ] 导弹拦截

题目:

https://www.luogu.org/problemnew/show/P1020

  具体思路:

    1.每次都用最低的拦截器去拦截导弹

    2.如果不够,新开一个,拦截高度定为当前高度

 

注:这里只给出了100分的做法(因为是贪心专题,就难得写了,重点在第二问)

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int High[100001][2], System[100001];
int main() {
    int Amount = 0;
    while (cin >> High[++Amount][0]) {
        High[Amount][1] = 1;
    };
    Amount--;
    for (int i = 2; i <= Amount; i++) {
        int l = 0, k = 0;
        for (int j = 1; j < i; j++) {
            if (High[j][0] >= High[i][0] && High[j][1] > l) {
                l = High[j][1];
            }
        }
        if (l) {
            High[i][1] = l + 1;
        }
    }
    int maxn = 0;
    for (int i = 1; i <= Amount; i++) {
        maxn = max(maxn, High[i][1]);
    }
    printf("%d\n", maxn);
    int Num = 0;
    System[++Num] = 50001;
    for (int i = 1; i <= Amount; i++) {
        int minn = 50001,pos = 0;
        for (int j = 1; j <= Num; j++) {
            if (System[j] >= High[i][0] && System[j] <= minn) {
                minn = System[j];
                pos = j;
            }
        }
        if (pos) {
            System[pos] = High[i][0];
        }
        else {
            System[++Num] = High[i][0];
        }
    }
    cout << Num;
    return 0;
}

  

 

posted @ 2018-05-27 23:06  WenOI  阅读(436)  评论(0编辑  收藏  举报
水波背景