码迷,mamicode.com
首页 > 其他好文 > 详细

贪心专题

时间:2015-05-15 21:33:24      阅读:218      评论:0      收藏:0      [点我收藏+]

标签:贪心   双端   dag   总结   

贪心:
原则是根据固定的一个或几个属性进行抉择.达到缩小规模的目的.
split to steps and shrink scope
有点双端/DAG图的意思.
reference


最近做的几个贪心题目
hdu 1052(田忌赛马)
根据马的速度排序,然后两边最小值,最大值的判断.这样做是因为两端的选择是唯一的,TJ最弱的马/最强的如果可以战胜那就是最好的抉择,不然就让其去当炮灰.(减少规模)

#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 1100000
using namespace std;
int A[maxn],B[maxn];

int main(){
    int N;
    while(~scanf("%d",&N),N){
        for(int i = 0;i < N;i++)
            scanf("%d",A+i);
        for(int i = 0;i < N;i++)
            scanf("%d",B+i);
        sort(A,A+N);
        sort(B,B+N);
        int sa=0,sb=0,ea=N-1,eb=N-1;
        int po=0,ne=0;
        for(int i = 0;i<N;i++){
            if(A[sa]>B[sb]){///TJ slowest more quick
                po++;sa++,sb++;
                continue;
            }else if(A[sa]<B[sb]){///QW slowest more quick TJ use lowest to complete it
                ne++;sa++;eb--;
                continue;
            }
            if(A[ea]>B[eb]){///TJ fastest more quick
                po++;ea--;eb--;
                continue;
            }else if(A[ea]<B[eb]){
                ne++;sa++;eb--;
                continue;
            }
            if(A[sa]<B[eb])
                ne++,sa++,eb--;
            else sa++,sb++;

        }
        printf("%d\n",200*(po-ne));
    }

    return 0;
}

hdu 4268 这个要用到multiset .
问题是A的矩形套B的矩形最多能套多少?
因为是两个属性,所以先按照一个属性升序排序,然后将该属性能覆盖的都放入二叉树中维护选择最”接近”的覆盖,这样就给后面的元素更多的空间.(这样先排序的原因是再也不用考虑这个属性了.且先将另一个元素最”接近”的使用掉)

#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 1100000
using namespace std;
struct P{
    int w,h;
    int tag;
    bool operator <(const P &x) const{
        if(w!=x.w) return w<x.w;
        if(h!=x.h) return h<x.h;
        return tag>x.tag;
    }
}A[maxn];
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int N;
        scanf("%d",&N);
        for(int i = 1;i<=N;i++)
            scanf("%d%d",&A[i].h,&A[i].w),A[i].tag = 1;
        for(int i = N+1;i<=N+N;i++)
            scanf("%d%d",&A[i].h,&A[i].w),A[i].tag = 2;
        sort(A+1,A+N+N+1);
        int ans = 0;
        multiset<int> st;
        for(int i = 1;i<=N+N;i++){
            if(A[i].tag==2) {st.insert(A[i].h);continue;}
            if(st.empty()) continue;
            if( A[i].h<*(st.begin()) ) continue;
            multiset<int>::iterator it = st.upper_bound(A[i].h);
            st.erase(--it);
            ans++;
        }
        printf("%d\n",ans);
    }

    return 0;
}

UVALive 7146 这个Shanghai坑点
乍一看,我靠和上面那个基本一样啊.但是这有个就是要“完全覆盖”
如果从小到大看,可能会有元素被后面元素覆盖而不需要”牺牲”
如果从大到小看,一个元素能够覆盖的范围是已知的,不会变更,它可以将更”难”的覆盖掉,将”简单”的留给后面.
典型
(1)区间完全覆盖问题
问题描述:给定一个长度为m的区间,再给出n条线段的起点和终点(注意这里是闭区间),求最少使用多少条线段可以将整个区间完全覆盖
样例:

区间长度8,可选的覆盖线段[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]

解题过程:

setp1 将每一个区间按照左端点递增顺序排列,拍完序后为[1,4],[2,4],[2,6],[3,5],[3,6],[3,7],[6,8]

setp2 设置一个变量表示已经覆盖到的区域。再剩下的线段中找出所有左端点小于等于当前已经覆盖到的区域的右端点的线段中,右端点最大的线段在加入,直到已经覆盖全部的区域

setp3 过程:

假设第一步加入[1,4],那么下一步能够选择的有[2,6],[3,5],[3,6],[3,7],由于7最大,所以下一步选择[3,7],最后一步只能选择[6,8],这个时候刚好达到了8退出,所选区间为3

setp4 贪心证明:

需要最少的线段进行覆盖,那么选取的线段必然要尽量长,而已经覆盖到的区域之前的地方已经无所谓了,(可以理解成所有的可以覆盖的左端点都是已经覆盖到的地方),那么真正能够使得线段更成的是右端点,左端点没有太大的意义,所以选择右端点来覆盖

(2)最大不相交覆盖(今年暑假不AC)
问题描述:给定一个长度为m的区间,再给出n条线段的起点和终点(开区间和闭区间处理的方法是不同,这里以开区间为例),问题是从中选取尽量多的线段,使得每个线段都是独立的,就是不和其它有任何线段有相交的地方

样例:

区间长度8,可选的覆盖线段[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]

解题过程:

对线段的右端点进行升序排序,每加入一个线段,然后选择后面若干个(也有可能是一个)右端点相同的线段,选择左端点最大的那一条,如果加入以后不会跟之前的线段产生公共部分,那么就加入,否则就继续判断后面的线段
1排序:将每一个区间按右端点进行递增顺序排列,拍完序后为[1,4],[2,4],[2,6],[3,5],[3,6],[3,7],[6,8]
2第一步选取[2,4],发现后面只能加入[6,8],所以区间的个数为2
贪心证明:因为需要尽量多的独立的线段,所以每个线段都尽可能的小,对于同一右端点,左端点越大,线段长度越小。那么为什么要对右端点进行排序呢?如果左端点进行排序,那么右端点是多少并不知道,那么每一条线段都不能对之前所有的线段进行一个总结,那么这就明显不满足贪心的最有字结构了。

(3)区间选点问题(poj1328)

问题描述:给定一个长度为m的区间,再给出n条线段和这n条线段需要满足的要求(要求是这n条线段上至少有的被选择的点的个数),问题是整个区间内最少选择几个点,使其满足每一条线段的要求.

解题过程:将每个线段按照终点坐标进行递增排序,相同终点的前点坐标大的在前面,一个个将其满足

贪心证明:要想使得剩下的线段上选择的点最少,那么就应该尽量使得已经选择了的点尽量能在后面的线段中发挥作用,而我们是从左往右选择线段的,那么要使得选取的点能满足后面线段的要求,那么必须是从线段的有端点开始选点,那么问题(2)一样涉及到一个问题,如果是按照线段的左端点对线段进行排序的话,不知道右端点的话,每一条线段都不能对之前已经操作过的所有线段进行一个总结,那么这就同样不满足贪心算法的最优子结构性质了。

可以解决的实际问题:数轴上面有n个闭区间[a,b],取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)

贪心专题

标签:贪心   双端   dag   总结   

原文地址:http://blog.csdn.net/gg_gogoing/article/details/45748533

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!