【贪心算法】

/*
POJ 3262
有n头牛在糟蹋庄稼。把第i头牛牵回家需要ti分钟。第i头牛每分钟会摧毁di的庄稼。每次只能牵一头牛走。问怎么牵使损失最少。
思路:考虑a,b两牛。先牵a牛和b牛的损失分别为。2*d[b]*t[a],2*d[a]*t[b]。设先牵a更优。2*d[b]*t[a]<2*d[a]*t[b].
所以根据优先级排序然后依次牵就是最优的选择。
1 在二个中间选择之中,能根据time/eat小的那个为最优解
证明:二个羊中 A,B,属性分别为分别为eatA,timeA,eatB,timeB
选A的时候损失timeA*eatB
选B的时候损失timeB*eatA
双方同除以eatA*eatB.
令time/eat为一个羊的比率x
可以证明x小的那个为最优解.

*/

#include<cstdio>
#include<algorithm>
#define MAXN 111111
#define MAXM 222222
const int INF=0x3f3f3f3f;
using namespace std;
struct node{
    int x,y;
}a[MAXN];
int n;
bool cmp(node x,node y){//乘积排序即可
    return x.x*y.y<x.y*y.x;
}
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d%d",&a[i].x,&a[i].y);
        a[i].x*=2;//一来一回需要时间两倍
    }
    sort(a,a+n,cmp);
    long long ans=0;
    long long sum=0;
    for(int i=0;i<n;i++){
        ans+=sum*(long long )a[i].y;
        sum+=a[i].x;//一直吃花的时间
    }
    printf("%lld\n",ans);
}


/*
HDU 3466
这是一道01背包的变形题,题目增加了一个限制条件,即当你所拥有的钱数大于某个限定值时才可以购买该物品。

按照q - p以由大到小的顺序排序,然后进行01背包的DP即可。

题意是: 给你一些钱 m ,然后在这个国家买东西, 共有 n 件物品,每件物品有  价格 P    价值 V    还有一个很特别的属性 Q, Q 指 你如过想买这件物品 你的手中至少有这钱Q 。 虽然你只要花费 钱P ,但你的手中至少有钱Q,如果不足Q ,不能买。问给你钱M ,列出N件物品,最多能获得多少价值的东西。。。。

  按 Q-P 的大小 ,从小到大排序,然后按照0-1 背包的思想
                              dp[c]=max(dp[c],dp[c-p[i]])+v[i];  最终 dp[m]就是结果。
为什么这样呢?A:p1,q1   B: p2,q2,先选A,则至少需要p1+q2的容量,而先选B则至少需要p2+q1,如果p1+q2>p2+q1,那么要选两个的话的就要先选A再选B,公式可换成q1-p1 < q2-p2,就按这样的方法排序最后的顺序就是最优的顺序。

*/

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=5005;
int dp[MAXN];
struct Node{
    int p,q,v;
}node[505];
bool cmp(Node a,Node b){
        return (a.q-a.p)<(b.q-b.p);//按照之差递减排序
}
int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        for(int i=0;i<=m;i++)
            dp[i]=0;
        for(int i=0;i<n;i++){
            scanf("%d%d%d",&node[i].p,&node[i].q,&node[i].v);
        }
        sort(node,node+n,cmp);
        for(int i=0;i<n;i++){//0、1背包问题
            for(int j=m;j>=node[i].p;j--){
                if(j>=node[i].q)
                    dp[j]=max(dp[j],dp[j-node[i].p]+node[i].v);
            }
        }
        int ans=0;//输出最大的值
        for(int i=0;i<=m;i++)
            if(ans<dp[i])
                ans=dp[i];
        printf("%d\n",ans);
    }
}

/*
POJ 1862(贪心+优先队列)
题意很简单,就是给定你一系列的数值,任意挑选其中两个数a和b,得到一个新的数2*sqrt(a*b),然后再将这个新的个放回。一直这样下去,直到只剩下一个数。求剩下的这个数最小是多少。

使用贪心策略,不断挑选两个最大的数组成一个新的数并放回原数列,再挑选最大的两个数……最到只乘一个数为止。

*/

#include<cstdio>
#include<queue>
#include<cmath>
using namespace std;
int main(){
    int n;
    scanf("%d",&n);
    double a,b,t;
    priority_queue<double>p;
    for(int i=0;i<n;i++){
        scanf("%lf",&t);
        p.push(t);
    }
    while(p.size()!=1){
        a=p.top();
        p.pop();
        b=p.top();
        p.pop();
        t=2*sqrt(a*b);
        p.push(t);
    }
    printf("%.3lf\n",p.top());
}

/*
POJ  3040
夫约翰要给奶牛Bessie发工资了(你们结婚吧,生个牛头人( ̄_ ̄|||) ),每周至少 C 元。约翰手头上有面值V_i的硬币B_i个,这些硬币的最小公约数为硬币的最小面值。求最多能发几周?
贪心策略是使多发的面额最小(最优解)。分三个阶段:

首先面额不小于C的硬币属于没办法节约的类型,先统统发掉。

然后对硬币面额从大到小尽量凑得接近C,允许等于或不足C,但是不能超出C。

接着按硬币面额从小到大凑满C(凑满的意思是允许超出一个最小面值,ps此处的最小面值指的是硬币剩余量不为0的那些硬币中的最小面值),凑满之后得出了最优解,发掉,进入步骤2.

这样就保证了每次都是当前的最优解,这个题很好地体现了贪心法的精髓。
*/

#include <cstdio>
#include<cstring>
#include <algorithm>
using namespace std;
const int INF =0x7fffffff;
typedef pair<int ,int > Coin;//货币 面值 数额
Coin coin[20];
int need[20];
int cmp(Coin x,Coin y){ //面值从大到小排序
    return x.first>y.first;
}
int main(){
    int N,C;
    scanf("%d%d",&N,&C);
    for(int i=0;i<N;i++)
        scanf("%d%d",&coin[i].first,&coin[i].second);
    int week=0;
    for(int i=0;i<N;i++){
        if(coin[i].first>=C){ // 面额不小于C的一定可以支付一周
            week+=coin[i].second;
            coin[i].second=0;
        }
    }
     sort(coin, coin + N,cmp);
    while(true){
        int sum=C;// 等待凑足的sum
        memset(need,0,sizeof(need));
        for(int i=0;i<N;i++){ // 从大到小
            if(sum>0&&coin[i].second>0){
                int can_use=min(coin[i].second,sum/coin[i].first);
                if(can_use>0){
                    sum-=can_use*coin[i].first;
                    need[i]=can_use;
                }
            }
        }
        // 没凑够的话。只能往回找最小的面值凑够  从小到大
        for(int i=N-1;i>=0;i--){
            if(sum>0&&coin[i].second>0){
                int can_use=min(coin[i].second-need[i],(sum+coin[i].first)/coin[i].first);
                if(can_use>0){
                    sum-=can_use*coin[i].first;
                    need[i]+=can_use;
                }
            }
        }
        if(sum>0)
            break;
        int add_up=INF;  //使用该种发货币的方式最多可以持续的周数
        for(int i=0;i<N;i++){
            if(need[i]==0)
                continue;
            add_up=min(add_up,coin[i].second/need[i]);
        }
        week+=add_up;
        for(int i=0;i<N;i++){
            if(need[i]==0)
                continue;
            coin[i].second-=add_up*need[i];
        }
    }
    printf("%d\n",week);
}
/*
题目:
POJ 2376
给定一个时间T和N个时间区间,求最少需要多少个区间覆盖总区间[1,T],无法覆盖区域[1,T]时输出-1。

例如T=10,有3个区间[1,7],[3,6],[8,10],则最少需要两个区间来覆盖,选择区间1和区间3。

解题思路:

使用贪心法。首先将区间按开始时间从小到大排序,开始时间相等按结束时间从小到大排序。
1 如果第一个区间不是从1开始,则无法覆盖,输出-1。
2 令当前覆盖到的时间time为开始时间为1的区间中结束时间的最大值。
3 从开始时间不为1的区间开始循环遍历。
4 选择合法区间中结束时间值最大的那个区间,合并到[1,time],合并后time为选择的区间的结束时间。所谓合法区间是指区间的起始时间start<=time+1,这样才能和区间[1,time]合并。如果没有找到合法区间或者找到的区间结束时间比time小,则无法覆盖,结束循环。
5 循环结束后,根据time是否大于等于T,输出结果。

*/

#include<cstdio>
#include<algorithm>
using namespace std;
typedef struct node{
    int start,endx;
}interval;
interval data[25005];
int cmp(node a,node b){//按start从小到大,end从小到大排序
    if(a.start==b.start)
        return a.endx<b.endx;
    return a.start<b.start;
}
int main(){
    int t,n,time,i,j,ans,max_end;

    scanf("%d %d",&n,&t);
    for( i=0;i<n;i++)
        scanf("%d %d",&data[i].start,&data[i].endx);
     sort(data,data+n, cmp);
    if(data[0].start!=1){//不是从1开始,无法覆盖
        printf("-1\n");
        return 0;
    }
    else{
        time=data[0].endx;
        i=1;//i指向下一个区间
        while(data[i].start==1){
            time=data[i].endx;i++;
        }
    }
    ans=1;
    while(time<t){
        //使用贪心原则查找下一个区间,取合法区间中结束时间最大的那个
        if(i>=n)
            break;
        j=i;
        max_end=data[i].endx;
        i++;
        while(i<n&&data[i].start<=time+1){//起点的小于等于time+1 因为区间不能间隔
            if(data[i].endx>max_end){
                j=i;
                max_end=data[i].endx;
            }
             i++;
        }
        if(max_end<=time || data[j].start>time+1)
            break;
    //结束时间没有增长或选择的区间为非法区间,无法覆盖
        else{//增加一个区间,当前时间变为选择的区间的结束时间
            ans++;
            time=data[j].endx;
        }
    }
    if(time<t)
        printf("%d\n",-1);
    else
        printf("%d\n",ans);

}

/*
POJ  3190
题意:有N条线段,求最少要将这些线段分成几组,使得每组中的线段都没有重叠
按左端点排序,然后每次挑之前分的组中右端点最左的那组,如果有重叠,就新开一组。
可用优先级队列进行维护(右端点最左的)


*/

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=600000;
int n,use[maxn];
struct Node{
    int l;//左端点
    int r;//右端点
    int pos;//位置
    //重载优先队列的小于号  结束时间早为优先 右断点相等,左端点小的在前
    bool operator <(const Node &a)const {
        if(r==a.r)
            return l>a.l;
        return r>a.r;
    }
}a[maxn];
priority_queue<Node> q;
//左端点按照从小到大排序
bool cmp(Node a,Node b){
    if(a.l==b.l)
        return a.r<b.r;
    return a.l<b.l;
}
int main(){
    while(~scanf("%d",&n)){
        for(int i=0;i<n;i++){
            scanf("%d%d",&a[i].l,&a[i].r);
            a[i].pos=i;
        }
    sort(a,a+n,cmp);
    q.push(a[0]);
    int ans=1;
    use[a[0].pos]=1;
    for(int i=1;i<n;i++){
        if(!q.empty()&&q.top().r<a[i].l){
            use[a[i].pos]=use[q.top().pos];
            q.pop();
        }
        else{
            ans++;
            use[a[i].pos]=ans;
        }
        q.push(a[i]);
    }
    printf("%d\n",ans);
    for(int i=0;i<n;i++)
        printf("%d\n",use[i]);

    while(!q.empty()){
        printf("%d  %d\n",q.top().r,q.top().l);
        q.pop();
    }
    }
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值