贪心算法

来源:互联网 发布:mysql 查看用户连接数 编辑:程序博客网 时间:2024/06/06 15:59
贪心算法
         贪心算法是指在对问题求解时,总是作出在当前看来是最好的选择。也就是说,不从整体上加以考虑,它所作出的仅仅是在某种意义上的局部最优解(是否是全局最优,需要证明)。
       如果我们要用贪心算法求解某问题的整体最优解,必须首先证明贪心思想在该问题的应用结果就是最优解!!
贪心算法的基本步骤:
         1、从问题的某个初始解出发。
    2、采用循环语句,当可以向求解目标前进一步时,就根据局部最优策略,得到一个部分解,缩小问题的范围或规模。
    3、将所有部分解综合起来,得到问题的最终解。

一、事件序列问题
        已知n个事件的发生时刻和结束时刻(见下表,表中事件已按结束时刻升序排序)。一些在时间上没有重叠的事件,可以构成一个事件序列,如事件{2,8,10}。事件序列包含的事件数目,称为该事件序列的长度。请编程找出一个最长的事件序列。


【算法分析】
       不妨用begin[i]和end[i]表示事件i的开始时刻和结束时刻。则原题的要求就是找一个最长的序列a1 < a2 < … < an,满足:
       begin[a1] < end[a1] <= … <= begin[an]< end[an]
       可以证明,如果在可能的事件a1 < a2 < … < an中选取在时间上不重叠的最长序列,那么一定存在一个包含a1(结束最早)的最长序列。(证明略)

【程序1】今年暑假不AC
        【问题描述】
       “今年暑假不AC?”
       “是的。”
       “那你干什么呢?”
       “看世界杯呀,笨蛋!”
       “@#$%^&*%...”

       确实如此,世界杯来了,球迷的节日也来了,估计很多OIer也会抛开电脑,奔向电视了。
       作为球迷,一定想看尽量多的完整的比赛,当然,作为新时代的好青年,你一定还会看一些其它的节目,比如新闻联播(永远不要忘记关心国家大事)、非常6+7、超级女生,以及韩国热播的《running man》等等,假设你已经知道了所有你喜欢看的电视节目的转播时间表,你会合理安排吗?(目标是能看尽量多的完整节目)
【输入】
       第一行只有一个整数n(n<=100),表示你喜欢看的节目的总数,然后是n行数据,每行包括两个数据Ti_s,Ti_e (0<=i<=n),分别表示第i个节目的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。
【输出】
       输出能完整看到的电视节目的个数。
【样例输入】
12
1 3
3 4
0 7
3 8
15 19
15 20
10 15
8 18
6 12
5 10
4 14
2 9
【样例输出】
5

【分析】按照每个节目的结束时间排序,目的是使剩余时间留下,再判断还能看几个节目。对节目时间的排序结束后,依次判断:这次要看的节目的开始时间是否大于上次看的节目的结束时间,若是大于等于,则这个节目可以完整观看;若是小于则不能完整观看,跳到下一个节目继续判断。
       对于样例数据,对结束时间进行排序后的数组如下:
1    3    0    3    2    5    6    4   10    8   15   15    3    4    7    8    9   10   12   14   15   18   19   20
然后进行依次判断发现有5个节目时可以完整观看的,即上面的5组红色字体,接着便输出结果。
【C++】#include<iostream>#include<algorithm>#include<cstring>using namespace std;int n;struct show{    int begin;    int end;}a[110];bool cmp(show a,show b){    return a.end<b.end;}int main(){cin >> n;int ans=1,i;memset(a,0,sizeof(a));for(i=0;i<n;i++) cin >> a[i].begin >> a[i].end;sort(a,a+n,cmp);int tmp=a[0].end;for(i=1;i<n;i++)if(a[i].begin>=tmp){ans++;tmp=a[i].end;}cout << ans << endl;return 0;}


二、部分背包问题
        给定n种物品和一个背包。物品i的重量是Wi,其价值为Vi,背包的容量为C。应如何选择装入背包的物品,使得装入背包中物品的总价值最大,可以选择物品i的一部分,而不一定要全部装入背包,1≤i≤n。

用贪心算法解背包问题的基本步骤:
       首先计算每种物品单位重量的价值Vi/Wi,然后,依贪心选择策略,将尽可能多的单位重量价值最高的物品装入背包。若将这种物品全部装入背包后,背包内的物品总重量未超过C,则选择单位重量价值次高的物品并尽可能多地装入背包。依此策略一直地进行下去,直到背包装满为止。
部分背包的算法框架如下:按单位重量价值v[i]/w[i]降序排列w[i]和v[i];将数组a[n]初始化为0;i=1;while (w[i]<=C){   将第i个物品放入背包:a[i]=1;   C=C-w[i];   i++}a[i]=C/w[i];程序代码片段:Sort(n,v,w);int i;for (i=1;i<=n;i++) a[i]=0;float c=M;for (i=1;i<=n;i++) {if (w[i]>c) break;a[i]=1; c=c-w[i];}if (i<=n) a[i]=c/w[i];


三、区间覆盖问题
        用i来表示x轴上坐标为[i-1,i]的区间(长度为1),并给出m(1<=M<=200)个不同的整数,表示m个这样的区间。现在让你画几条线段覆盖住所有的区间,条件是:每条线段可以任意长,但是要求所画线段之和最小,并且线段的数目不超过n(1<=N<=50)。
例如:m=5个整数1、3、4、8和11表示区间,要求所用线段不超过n=3条

【分析】如果n>=m,那么显然用m条长度为1的线段可以覆盖住所有的区间,所求的线段总长为m。
       如果n=1,那么显然所需线段总长为:…
       如果n=2,相当于n=1的情况下从某处断开(从哪儿断开呢?)。
       如果n=k呢?


【程序2】移动桌子 (laoj1333)
        著名的ACM(先进的电脑制造商)公司租了一个楼层,形状如下图。

       这层楼中间有个走廊,走廊的南面和北面各有200各房间。最近公司制定了一个计划,对这层楼进行改造。改造包括从一些房间中移动一些桌子到另外一些房间。因为楼道很窄,桌子很大,每次只有一张桌子可以通过走廊。为了不浪费时间,移动桌子的这个计划需要高效处理。经理想出了以下做法:搬动一张桌子从一个房间到另一个房间可以在10分钟内完成。当从一个房间i移动一张桌子到房间j时,房间i到房间j的这部分走廊就被占用。所以,在每10分钟时间内,同时移动的几张桌子不能共享它们所经过的走廊。经理明确说明了同时移动的可能情况和不可能情况。

       对于每一个房间,只有一张桌子可以移入或移出。现在,经理寻求以最少的时间去移动所有的桌子。你的任务是写一个程序来解决经理的问题。
输入
       第一行包含一个整数n,1≤n≤200,表示要把桌子的数量。以下n行包含两个正整数s和t,代表一张桌子是从房间s搬到房号t(每个房间号码最多出现一次)。
输出
       输出完成移动的最小分钟数。
样例输入
3
4
10 20
30 40
50 60
70 80
2
1 3
2 200
3
10 100
20 80
30 50
样例输出
10
20
30

【分析】
1、如果没有交叉,总时间应该是多少?
2、影响搬运时间的因素是什么?
3、如果每趟处理都包含最大重叠,处理后的效果是什么?
4、得出什么结论?


其实,贪心策略是这样的:
       对门其实考虑成一个点,故只要200个就可,然后统计经过每个点的次数,次数最大即为需要的最短时间。
C++语言描述如下:#include<iostream>#include<cstring>using namespace std;int main(){int i,j,n,a[201];int s,d,t,k,ans=-1; memset(a,0,sizeof(a)); cin >> n; for(j=0;j> s >> d; s=(s-1)/2; d=(d-1)/2;                if(s>d){t=s;s=d;d=t;} for(k=s;k<=d;k++) a[k]++;     } for(j=0;j<200;j++) if(a[j]>ans) ans=a[j];     cout << ans*10 << endl;    return 0; }


四、单源最短路径
        其基本思想是,设置顶点集合S并不断地作贪心选择来扩充这个集合。一个顶点属于集合S当且仅当从源到该顶点的最短路径长度已知。
       初始时,S中仅含有源。设u是G的某一个顶点,把从源到u且中间只经过S中顶点的路称为从源到u的特殊路径,并用数组dist记录当前每个顶点所对应的最短特殊路径长度。Dijkstra算法每次从V-S中取出具有最短特殊路长度的顶点u,将u添加到S中,同时对数组dist作必要的修改。一旦S包含了所有V中顶点,dist就记录了从源到所有其它顶点之间的最短路径长度。
0 1
原创粉丝点击