贪心题例

贪心各种问题:

 

贪心思想在许多算法中都有体现。例如,图论中求最短路的Dijkstra算法,求最小生成树的Prim算法和Kruskal算法都是典型的贪心算法。

    贪心思想的理论部分不难理解,关键就在于面对不同问题的时候,能否准确地发现问题的最优策略。对于简单的题目而言,贪心策略往往是显而易见的,但更多的情况下,贪心策略是需要通过分析很多推理得到的。

     如:给出n个数{a_n},每次都从这些数中选择两项,记为x和y,将它们删去,并增加一个新的数xy+1。显然,每次操作都会使数的个数减少一,那么n-1次操作之后,就只剩下一个数了。试问这个数最大是多少。

    每次选择什么样的两个数才能够使得最后剩下的数最大呢?

    如果一下子难以回答这个问题,那就先回答下面这个问题:

    有三个数a,b,c,它们满足a

    从简单的子问题出发,我们便得到了原问题的最优策略的一个合理猜想:每次都选择最小的两个数进行操作。

    ②

  有n个人需要在夜里过河,河上有一座桥,这个桥最多只能承受两个人。这n个人只有一个手电筒,想要过桥就必须有一个人拿着手  电筒。然而,这n个人过桥所需要的时间是不一样的,分别记为a_1,a_2,…,a_n,当两个人同时过桥时,所消耗的时间等于这两个人过桥所需要的时间中较长的那一个,即迁就较慢的人。试问,这n个人全部过桥最少需要多长时间?

  如果觉得这个问题很棘手,那么来考虑以下的问题:

   1.如果只有三个人,他们过河的时间依次是a_1,a_2,a_3,且满足a_1

   2.考虑四个人,他们过河的时间依次是a_1,a_2,a_3,a_4,且满足a_1

 

   3.核心想法:尽量减少为了将手电筒送回来而浪费的时间,于是就产生了两个可能的最优策略。

 

分析:

 

假如现在只有三个人的话,过河时间分别为a1,a2,a3,且a1<=a2<=a3,则过河最短时间一定是a1+a2+a3(前提是,河对岸没有人的情况下),这只是作为一个参考模型。
现在多了一个人,好不疑问,必须要把4个人的情况,降低为3个人,且必须经历这样一个过程,因此在让第一个人过河时,必须使得时间最短。现在,把4个人的速度排序1,2,5,10。前两个前先过,时间当然是最短的,毫无疑问,2在对岸,1返回,共耗时3分钟。
好了,现在,问题回到了最初的模型,3人过河的问题,但唯一不同的是,河对岸现在是有人的,因此不能和之前的情况完全的相同。必须分情况。按什么分呢,显然是剩余三人的过河时间。
第一:现在我们忽略河对岸的人,完全按照最初的模型来进行计算,显然时间为1+5+10=16分钟;
第二:我们利用河对岸的人,并以此为资源,缩短过河时间。分析如下:时间最长的人,无论如何都要花费那么多的时间,因此让时间第二长的人和他一起过河,可以将第二人的时间完全的屏蔽掉,因此让5,10一起过河,共花费10分钟,此时1还在对岸,需要一人回去接应,当然此时应该让2去,因此,此过程花费4分钟。
把两种情况做简单的对比;第一种:3+16=19;第二种:3+10+4=17;
显然第二种要好。故共花费17分钟。
但是,有一点需要说明的是,并不是所有的情况都是第二种好。

 

比如1,3,4,5;

http://poj.org/problem?id=1700
分析:

当人数小于等于3时,直接算出,大于等于4时,取两种贪心策略的最小值

核心代码:

 

sort(time,time+n);
        int sum=0;
        while(1)
        {
            if(n==1) { sum+=time[0];break;}
            else if(n==2) { sum+=time[1]; break;}
            else if(n==3) { sum+=time[0]+time[1]+time[2];break;}
            else
            {
              sum+=min(2*time[1]+time[n-1]+time[0],2*time[0]+time[n-2]+time[n-1]);  //取两种贪心策略的最小值,先让最后两名过河
              n-=2;
            }
        }
        cout<

 

 

 

 

 

国王游戏(noip2012)

 

Description

恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。

国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

Input

第一行包含一个整数 n,表示大臣的人数。

第二行包含两个整数a和b,之间用一个空格隔开,分别表示国王左手和右手上的整数。

接下来n行,每行包含两个整数a和b,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。

Output

输出只有一行,包含一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的

金币数。

Sample Input

3

1 1

2 3

7 4

4 6

Sample Output

2

HINT

 

【输入输出样例说明】

按 1、2、3号大臣这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;

按 1、3、2这样排列队伍,获得奖赏最多的大臣所获得金币数为2;

按 2、1、3这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;

按 2、3、1这样排列队伍,获得奖赏最多的大臣所获得金币数为 9;

按 3、1、2这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;

按 3、2、1这样排列队伍,获得奖赏最多的大臣所获得金币数为 9。

因此,奖赏最多的大臣最少获得 2 个金币,答案输出 2。

 

【数据范围】

对于20%的数据,有1≤ n≤ 10,0 < a、b < 8;

对于40%的数据,有1≤ n≤20,0 < a、b < 8;

对于60%的数据,有1≤ n≤100;

对于60%的数据,保证答案不超过 10^9;

对于100%的数据,有 1 ≤ n ≤1,000,0 < a、b < 10000。

 


分析:

 

正解= 贪心+高精.
贪心证明 :
设存在 相邻大臣A(l1,r1),B(l2,r2),A,B前左手乘积为Sum :
   当A在B前时:
        则Ans=max(Sum/r1,Sum*l1/r2) ;
  当B在A前时:
        则Ans=max(Sum/r2,Sum*l2/r1) ;
  显然 Sum*l2/r1>Sum/r1  ;
         Sum*l1/r2>Sum/r2 ;

比较  Sum*l2/r1 、Sum*l1/r2  求两种情况下最大值中的最小值

   ①当 Sum*l2/r1>Sum*l1/r2  (第一种最大值Sum*l1/r2,第二种最大值 Sum*l2/r1)
        即   l2*r2>l1*r1 时 ,选择第一种(最大值为Sum*l1/r2较小)即A应在B前
   ②同理

当 Sum*l2/r1  

           即   l2*r2   所以按 l*r 从小到大排序在进行模拟即为正解.

 

1 #include

  2 #include

  3 #include

  4 #include<string>

  5 #include

  6 #include

  7#define INF 999999999999

  8#define LL long long

  9#define Max(x,y) if(x

 10#define N 100003

 11usingnamespace std ;

 12struct P{

 13    LL p,t;

 14    P():p(),t(){}

 15    P(const LL x,const LL y) : p(x),t(y){}  

 16 }army[N],edge[N];

 17bool cmp(const P&x,const P &y){

 18    returnx.t>y.t;

 19 }

 20 LL n,m,T;

 21 LL next[N*2],last[N*2],to[N*2];

 22 LL w[N*2];

 23 LL p[N],from[N],root[N];

 24 LL dis[N],left[N];

 25bool use[N];

 26 queue q;

 27 inlinevoid addedge(LL a,LL b,LL c){

 28    next[++T]=last[a]; last[a]=T;to[T]=b ; w[T]=c;

 29    next[++T]=last[b]; last[b]=T;to[T]=a ; w[T]=c;

 30 }

 31void fail(){

 32    LL Tot=0;

 33    for(LLi=last[1];i;i=next[i]) ++Tot;

 34    if(Tot>m){

 35        printf("-1");

 36        exit(0);

 37    }

 38 }

 39 LL pushup(LL now,LL f){

 40     if(!next[last[now]]) {

 41        if(left[now]>=0ll)use[now]=1 ;

 42        returnleft[now]  ;

 43     }

 44     use[now]=1;

 45     for(LLi=last[now];i;i=next[i])

 46      if(to[i]!=f){

 47        Max(left[now],pushup(to[i],now)-w[i]);

 48         if(!use[to[i]])use[now]=0;

 49      }

 50     if(left[now]>=0) use[now]=1;

 51     returnleft[now];

 52 }

 53bool check(LL lim){

 54    LL numa=0,numb=0;

 55    memset(left,-1,sizeof left);

 56    memset(use,0,sizeof use);

 57    memset(from,0,sizeoffrom);

 58    for(LL i=1;i<=m;i++)

 59      if(dis[p[i]]>=lim)

 60        left[p[i]]=lim;

 61      else

 62        army[++numa]=P(root[p[i]],lim-dis[p[i]]);

 63    pushup(1,0);   

 64    for(LLi=last[1];i;i=next[i])

 65      if(!use[to[i]])

 66        edge[++numb]=P(to[i],w[i]);

 67    if(numb>numa)returnfalse;//军队不够

 68    sort(army+1,army+numa+1,cmp);

 69    sort(edge+1,edge+numb+1,cmp);

 70    //----来自该节点的所剩时间最少的军队---- 

 71    for(LLi=numa;i;i--)

 72       if(!from[army[i].p])

 73         from[army[i].p]=i;

 74    //--------------------------------------

 75    LL la=1,lb=1;

 76    memset(use,0,sizeof use);

 77    use[0]=1;

 78    while(lb<=numb&&la<=numa){

 79       if(use[la]){

 80          ++la;

 81          continue;   

 82       }

 83       if(!use[from[edge[lb].p]]){

 84          use[from[edge[lb].p]]=1;

 85          ++lb;

 86          continue ;

 87       }

 88       if(army[la].treturnfalse ;

 89       use[la]=1;

 90       ++la ;

 91       ++lb ;

 92    }

 93    returntrue ;

 94 }

 95void prework(){

 96    q.push(1);

 97   use[1]=1;

 98   for(LLi=last[1];i;i=next[i]) root[to[i]]=to[i];

 99   while(!q.empty()){

100        LL now=q.front();

101        q.pop();

102        for(LL i=last[now];i;i=next[i])

103        if(!use[to[i]]){

104            if(now!=1)root[to[i]]=root[now];

105            dis[to[i]]=dis[now]+w[i];

106            use[to[i]]=1;

107            q.push(to[i]);

108        }

109   }

110 }

111int main(){

112    //freopen("xx.in","r",stdin);

113    //freopen("xx.out","w",stdout);

114    scanf("%I64d",&n);

115    LLa,b,c;

116    for(LL i=1;i

117        scanf("%I64d%I64d%I64d",&a,&b,&c);

118        addedge(a,b,c);

119    }

120    scanf("%I64d",&m);

121    fail();

122    for(LL i=1;i<=m;i++) scanf("%I64d",&p[i]);

123    LLans,l=0,r=1000000000000LL;

124    prework();

125    while(l<=r){

126        LL mid=(l+r)>>1;

127        if(check(mid)){

128            ans=mid;

129            r=mid-1;

130        }else l=mid+1;

131    }

132    printf("%I64d",ans);   

133 }

 

 

 

你可能感兴趣的:(算法)