贪心算法小结

1、摇摆序列
题目: 若整数序列相邻元素的差 正负交替出现则其为摇摆序列。给定随机序列,求序列满足摇摆序列定义的最长子序列长度。
思路: 当出现连续递增或递减时,为形成摇摆子序列只需保留连续的递增或递减的首尾元素,使得尾部的后一个元素成为摇摆子序列的下个元素。
定义三个状态:起始、上升、下降,以及当前数字与所存储数字作比较。
代码:

int main(){
    int n,in[maxn];
    cin>>n;
    for (int i=0;i<n;i++) cin>>in[i];
    if (n<2) cout<<n; //必为上升/下降序列
    else{
        int maxlen=2,nownum=in[1],statue;
        if (in[1]>in[0]) statue = 1;//表示上升
        else statue=-1; //表示下降
        for (int i=2;i<n;i++){
            if (statue==1 && in[i]<nownum){ //上升且摇摆
                statue=-1;
                maxlen+=1;
            }
            if (statue==-1 && in[i]>nownum){ //下降且摇摆
                statue=1;
                maxlen+=1;
            }
            nownum=in[i];//要比较的数字要随时更新
        }
        cout<<maxlen;
    }
    return 0;
}

2、移除k个数字
题目: 使用字符串表示非负整数num,将num中的k个数字移除,求可获得的最小可能的新数字。
思路: 从高位向低位遍历,如果对应的数字大于下一位则把该位去掉,则得到的数字最小。
用栈存储最终结果,从高位向低位遍历num,若该数字大于栈顶元素,将其push入栈;若小于栈顶元素则pop出栈,直到栈空或不能再删除数字。最终栈中从栈底到栈顶存储的数字,即为结果。
代码:

int main(){
    string in;
    int k,i=1,cnt=0;
    cin>>in>>k; //输入字符串及可删除数字个数

    stack<char> num;
    num.push(in[0]);
    while(i<in.size()){//没有遍历完
        if (cnt<k && in[i]<num.top()){//还可以删去
            num.pop();
            cnt++;
        }
        num.push(in[i++]);
    }
    //输出结果
    if (k==in.size()) cout<<0;
    else{
        //从栈底到栈顶是结果
        string result="";
        while (num.size()){
            char c = num.top();
            result=c+result;
            num.pop();
        }
        //去掉前导零
        i=0;
        for (;i<result.size();i++){
            if (result[i]!='0') break;
        }
        for (int j=i;j<result.size();j++) cout<<result[j];
    }
    return 0;
}

3、活动选择问题
题目: 有n个需在同一天使用同一个教室的活动a1,a2,…,an,教室同一时刻只能由一个活动使用。每个活动ai都有开始时间si和结束时间fi,即[si,fi)。安排这些活动使尽量多的活动能不冲突地举行。
思路: 活动结束时间升序排列,使可开始时间和当前活动的开始时间比较,选择可执行活动。
代码:

#include <iostream>
#include <algorithm>
using namespace std;

struct act{int start,ending;};
bool cmp(act a,act b){return a.ending<b.ending;};//选择活动结束时间最早的活动
int main(){
    int n,cnt=1,time;
    cin>>n;
    act in[n];
    for (int i=0;i<n;i++) cin>>in[i].start>>in[i].ending;
    sort(in,in+n,cmp);
    time=in[0].ending; //最早可开始时间
    for (int i=1;i<n;i++){
        if (in[i].start>=time){ //下个活动可以执行
            time = in[i].ending;
            cnt += 1;
        }
    }
    if(n) cout<<"最多可执行的次数:"<<cnt;
    else cout<<0; //没有活动
    return 0;
}

4、钱币找零问题
题目: 假设1元、2元、5元、10元、20元、50元、100元的纸币分别有c0, c1, c2, c3, c4, c5, c6张。现在要用这些钱来支付K元,至少要用多少张纸币?
思路: 尽量选择面值大的纸币即可,至多要用多少张则尽量选择面值小的纸币。
代码:

#include <iostream>
#include <algorithm>
using namespace std;
struct money{int value,num;};

bool cmp(money a,money b){return a.value>b.value;};
int main(){
    int number,cnt;
    money wallet[7];
    for (int i=0;i<7;i++) cin>>wallet[i].value>>wallet[i].num;
    sort(wallet,wallet+7,cmp);
    while (cin>>number){
        cnt=0;
        for (int i=0;i<7;i++){
            while (number>=wallet[i].value){
                number -= wallet[i].value;
                cnt += 1;
            }
        }
        cout<<"共需使用"<<cnt<<"张纸币"<<endl;
    }
    return 0;
}

5、区域覆盖问题
题目: 假设海岸线是无限延伸的直线,陆地在海岸线一侧海洋在另一侧,每个小岛是海洋上的一个点。雷达坐落于海岸线上,只能覆盖d距离,所以如果小岛能够被覆盖到的话,它们之间的距离最多为d。题目要求计算出能够覆盖给出的所有岛屿的最少雷达数目。对于每个小岛,我们可以计算出一个雷达所在位置的区间。
思路: 用两点间距离公式计算出x轴上的各个区间,转换为求最少区间个数的问题,按右端点升序,用贪心策略找左被包含右最大的区间即可。
关键代码:

#include <algorithm>
#include <cmath>
struct node{double x,y;};
node point[1001];
bool cmp(node a,node b){return (a.x<b.x);}
int main(){
	int n,d,x,y,cnt,num=1;
	while(cin>>n>>d){ //点个数和雷达范围
		cnt=1;
		if (n==0 && d==0) break;
		for (int i=0;i<n;i++){
			cin>>x>>y;
			if (y>d)  cnt=-1; //雷达永远覆盖不了
			double t=sqrt(d*d-y*y); //转化为最少区间的问题
			point[i].x=x-t; //区间左端点
			point[i].y=x+t; //区间右端点
		}
		if(cnt!=-1){
			sort(point,point+n,cmp); //按区间左端点排序
			double s=point[0].y; //区间右端点
			for(int i=1;i<n;i++){
				if(point[i].x>s) cnt++; //两区间没有重合,增加雷达数目
				s=point[i].y; //更新右端点
			}
		}
		cout<<"Case "<<num<<':'<<' '<<cnt<<endl;
		num++;
	}
	return 0;
}

6、销售比赛
题目: 有偶数天,要求每天必须买一件物品或卖一件物品,只能选择一种操作并且不能不选,开始手上没有这种物品。现在给每天的物品价格表,要求计算最大收益。第一天必须买,最后一天必须卖,并且最后手上没有物品。
思路: 除了第一天和最后一天,每两天一组,价格低的买高的卖,并将卖的价格放入【最小堆priority_queue】,买的价格比堆顶还大就交换,保证买卖一定取得最大的收益价差。
关键代码:

#include <queue>
#include <cmath>
#define maxn 100001
using namespace std;
typedef long long ll;
ll price[maxn],res;
int main(){
    int t,n,res; cin>>t;
    while (t--){
        cin>>n; //总共天数
        priority_queue<ll,vector<ll>,greater<ll> > q;//优先队列构造最小堆
        for (int i=1;i<=n;i++) cin>>price[i];
        res=price[n]-price[1]; //最终收益
        for (int i=2;i<=n-1;i+=2){
            //两天一组进行买卖
            ll buy = min(price[i],price[i+1]);
            ll sell = max(price[i],price[i+1]);
            if (!q.empty() && buy>q.top()){//买的价格比卖的价格高
                res=res-2*q.top()+buy+sell;
                q.pop();
                q.push(buy);
            }
            else res=res-buy+sell;
            q.push(sell);
        }
        cout<<res<<endl;
    }
    return 0;
}

7、最短路径
(1)Floyd任何点到任何点
题目: 旅游路线图会知道城市间的高速公路长度及该公路要收取的过路费。现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径。如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。
思路: 最短路径+计算条数+计算费用(结构体)
关键代码:

#include <bits/stdc++.h>
#define MAX 99999999
#define maxn 505
struct edge{int len,much;};
edge cost[maxn][maxn],a[maxn][maxn];
//距离及费用初始化
void init(int n){
    int i,j;
    for (i=0;i<n;i++){
        for (j=0;j<n;j++){
            if (i==j) cost[i][j].len=0,cost[i][j].much=0;
            else cost[i][j].len=MAX,cost[i][j].much=MAX;
        }
    }
}
void floyd(int n){
    int i,j,k;
    for (i=0;i<n;i++){
        for (j=0;j<n;j++){
            a[i][j].len=cost[i][j].len;
            a[i][j].much=cost[i][j].much;
        }
    }
    //三重循环找最短路径和最少花费
    for (k=0;k<n;k++){
        for (i=0;i<n;i++){
            for (j=0;j<n;j++){
                if (a[i][k].len+a[k][j].len<a[i][j].len){
                    a[i][j].len=a[i][k].len+a[k][j].len;
                    a[i][j].much=a[i][k].much+a[k][j].much;
                }
                else if (i!=k&&k!=j&&a[i][k].len+a[k][j].len==a[i][j].len){//判断条件不能少 1!=j!=k
                    if (a[i][j].much>a[i][k].much+a[k][j].much) a[i][j].much=a[i][k].much+a[k][j].much;
                }
            }
        }
    }
}
int main(){
    int n,m,s,b,x,y,z,c,i;
    scanf("%d %d %d %d",&n,&m,&s,&b);
    init(n);
    for (i=0;i<m;i++){
        scanf("%d %d %d %d",&x,&y,&z,&c);
        if (cost[x][y].len>z){//多条路记录最短长度
            cost[x][y].len=z,cost[y][x].len=z;
            cost[x][y].much=c,cost[y][x].much=c;

        }
        else if (cost[x][y].len==z){
            if (cost[x][y].much>c) cost[x][y].much=c,cost[y][x].much=c;
        }
    }
    floyd(n);
    printf("%d %d",a[s][b].len,a[s][b].much);
    return 0;
}

(2)Dijkstra一个点到任何点
题目: 地图上有每个城市的救援队数量和每条连接两个城市的快速道路长度。当其他城市有紧急求助电话时,带领你的救援队尽快赶往事发地,同时一路上召集尽可能多的救援队.
思路: 最短路径+计算条数+顶点权重
关键代码:

#include <bits/stdc++.h>
#define MAX 99999999
#define maxn 505
int cost[maxn][maxn],pre[maxn],visit[maxn],dis[maxn],sum[maxn],cnt[maxn],num[maxn];
//初始化花费,访问,权重,距离和前驱节点
void init(int n){
    int i,j;
    for (i=1;i<=n;i++){
        for (j=1;j<=n;j++){
            if (i==j) cost[i][j]==0;
            else cost[i][j]=MAX;
        }
        visit[i]=0,sum[i]=0,pre[i]=i,cnt[i]=0,dis[i]=MAX;
    }
}
void Dij(int n,int s,int d){
    int i,j,maxi,mini;
    for (i=1;i<=n;i++){
        maxi=MAX,mini=s;
        for (j=1;j<=n;j++){//找距离向量中最短的点作为中间点
            if (visit[j]==0 && maxi>dis[j]){
                mini=j,maxi=dis[j];
            }
        }
        visit[mini]=1;
        for (j=1;j<=n;j++){
            //更新距离向量
            if (visit[j]==0 && dis[j]>dis[mini]+cost[mini][j]){
                dis[j]=dis[mini]+cost[mini][j];
                pre[j]=mini;
                cnt[j]=cnt[mini];
                sum[j]=sum[mini]+num[j];
            }
            else if(visit[j]==0 && dis[j]==dis[mini]+cost[mini][j]){
                cnt[j]+=cnt[mini];
                if (sum[j]<sum[mini]+num[j]){//距离相同找最多救援队
                    pre[j]=mini;
                    sum[j]=sum[mini]+num[j];
                }
            }
        }
    }
}
void print(int i){//路径打印
    if (pre[i]==i)return;
    print(pre[i]);
    printf("%d ",pre[i]);
}
int main(){
    //初始化输入
    int n,m,s,d,x,y,z,i;
    scanf("%d %d %d %d",&n,&m,&s,&d);
    init(n);
    for (i=1;i<=n;i++) scanf("%d",&num[i]);
    for (i=0;i<m;i++){
        scanf("%d %d %d",&x,&y,&z);
        if (cost[x][y]>z) cost[x][y]=z,cost[y][x]=z;
    }
    //计算
    visit[s]=1,dis[s]=0,cnt[s]=1,sum[s]=num[s];
    Dij(n,s,d);
    //结果输出
    printf("%d %d\n",cnt[d],sum[d]);
    print(d),printf("%d",d);
    return 0;
}

(3)bellman-ford:允许存在负值路径
题目: 地图上有每个城市的救援队数量和每条连接两个城市的快速道路长度。当其他城市有紧急求助电话时,带领你的救援队尽快赶往事发地,同时一路上召集尽可能多的救援队.
思路: 最短路径+计算条数+顶点权重
关键代码:

8、最小生成树和次小生成树
(1)Prim
题目:
思路:
关键代码:

(2)加入次小生成树
题目:
思路:
关键代码:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值