Ka贪心大暴走

【例 4 】均分纸牌( NOIP2002
        有 N 堆纸牌,编号分别为 1,2,…, N。每堆上有若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若干张纸牌,然后移动。
        移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 的堆上;在编号为 N 的堆上取的纸牌,只能移到编号为 N-1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。
        现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。
例如 N=4,4 堆纸牌数分别为:  ① 9 ② 8 ③ 17 ④ 6
        移动3次可达到目的:
        从 ③ 取4张牌放到④(9 8 13 10)->从③取3张牌放到 ②(9 11 10 10)-> 从②取1张牌放到①(10 10 10 10)。
【输入格式】
       N(N 堆纸牌,1 <= N <= 100)
       A1 A2 … An (N 堆纸牌,每堆纸牌初始数,l<= Ai <=10000)
【输出格式】
       所有堆均达到相等时的最少移动次数。
【样例输入】Playcard.in
    4
    9 8 17 6
【样例输出】Playcard.out
    3
 1 #include <stdio.h>
 2 int main()
 3 {
 4     int i,n,a[105],sum=0;
 5     scanf("%d",&n); //输入纸牌堆数 
 6     for(i=1;i<=n;i++)//输入各堆牌数 弃置0堆 
 7     {
 8         scanf("%d",&a[i]);
 9         sum+=a[i];
10     }
11     sum/=n;//求平均数
12     for(i=1;i<=n;i++)
13         a[i]-=sum;
14     //极端情况出现 0 0 0 x x 0 x x 0 0 x 0 0 0 0这种
15     //现在就见0直接往前挪  不是0把牌丢给右边 负数相当于逆向移牌
16     //如-3移到1变成0 -2,相当于1移3张给-3 
17     i=1;
18     sum=0;
19     while(i<=n)
20     {
21         if(a[i]==0)
22         {
23             i++;
24         }
25         else
26         {
27             a[i+1]+=a[i];
28             a[i]=0;
29             i++;
30             sum++;
31         }
32     } 
33     printf("%d",sum);
34     return 0;
35 }
 
 
【例 5 】删数问题( NOI94
  输入一个高精度的正整数N,去掉其中任意S个数字后剩下的数字按原左右次序组成一个新的正整数。编程对给定的N和S,寻找一种方案使得剩下的数字组成的新数最小。
       输出新的正整数。(N不超过240位)输入数据均不需判错。
【输入】
       n
       s
【输出】
       最后剩下的最小数。
【样例输入】
       175438
       4
【样例输出】
       13
【算法分析】
       由于正整数n的有效数位为240位,所以很自然地采用字符串类型存贮n。那么如何决定哪s位被删除呢?是不是最大的s个数字呢?显然不是,大家很容易举出一些反例。为了尽可能逼近目标,我们选取的贪心策略为:每一步总是选择一个使剩下的数最小的数字删去,即按高位到低位的顺序搜索,若各位数字递增,则删除最后一个数字;否则删除第一个递减区间的首字符,这样删一位便形成了一个新数字串。然后回到串首,按上述规则再删下一个数字。重复以上过程s次为止,剩下的数字串便是问题的解了。
 1 #include <stdio.h>
 2 #include <string.h>
 3 int main()
 4 {
 5     char a[250]; //存长数 
 6     int s,len,k,i=0;  //i为当前选择的位置 
 7     scanf("%s",a);
 8     scanf("%d",&s);
 9     len=strlen(a);
10     
11     while(a[i]=='0') i++; //去除首0
12     //我们现在举个例子,2293620,删4次 
13     //原理是删第一个单调子段的最大数
14     //第一个单调子段【相等也视为单调】 229
15     //删掉9 -> 223620
16     //删掉3 -> 22620
17     //删掉6 -> 2220
18     //删掉第一个2 -> 220
19     //整个过程就是这样
20     while(s--)
21     {
22         if(a[i]>a[i+1]) //如果是单调递减子段,直接删第一个 
23         {
24             i++;
25         }
26         else if(a[i]<=a[i+1]) //单调递增子段查找最后一个字符,注意用<= 
27         {
28             k=i+1; //i不动,k去找 
29             while(a[k]<=a[k+1])    k++; //这个k最终到达末尾
30             while(k+1<len) //从前往后挪,挪到最后一个数是k+1 
31             {
32                 a[k]=a[k+1]; 
33                 k++; 
34             }
35             len--; //减少字符长度         
36         }
37     }
38     for(;i<len;i++)
39         printf("%c",a[i]); 
40      
41     return 0;
42 }

 

 

【例 6 拦截导弹问题( NOIP1999
        某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统,但是这种拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,由于该系统还在试用阶段。所以一套系统有可能不能拦截所有的导弹。
       输入导弹依次飞来的高度(雷达给出的高度不大于30000的正整数)。计算要拦截所有导弹最小需要配备多少套这种导弹拦截系统。
【输入格式】
        n颗依次飞来的高度(1≤n≤1000).
【输出格式】
       要拦截所有导弹最小配备的系统数k。
【输入样例】 missile.in
       389  207  155  300  299  170  158  65
【输出样例】 missile.out
        2
【输入输出样例】
输入:导弹高度: 7  9   6  8  5
输出:导弹拦截系统K=2
输入:导弹高度: 4  3  2
输出:导弹拦截系统K=1
 1 #include <stdio.h>
 2 int main()
 3 {
 4 //    freopen("missile.in","r",stdin);
 5 //    freopen("missile.out","w",stdout);
 6     int flag,sumj=1,height,jacket[1005],i;
 7     scanf("%d",&height);
 8     jacket[0]=height;//先把第一个导弹拦截下来 
 9     while(scanf("%d",&height)==1)//你懂的 
10     {
11         flag=1;
12         for(i=0;i<sumj;i++)
13         {
14             if(jacket[i]==height) break;//如果有遇到高度相同自然是不用再准备一套的 
15             else if(jacket[i]>height)//一个一个找,找到就降低高度并跳出 
16             {
17                 jacket[i]=height;
18                 flag=0;
19                 break;
20             }
21         }
22         if(flag) //如果上面都没找到就准备新的导弹 
23         {
24             sumj++;
25             jacket[i]=height;
26         }
27     }
28     printf("%d",sumj);
29     return 0;
30 }

 

 

你可能感兴趣的:(Ka贪心大暴走)