信息学奥赛一本通(C++版) 第二部分 基础算法 第六章 贪心算法

总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716

信息学奥赛一本通(C++版) 第二部分 基础算法 第六章 贪心算法

不适合初学者做的题如下 2018-5-17

1321 【例6.3】删数问题(Noip1994)
1322 【例6.4】拦截导弹问题(Noip1999)
1324 【例6.6】整数区间
1224 最大子矩阵
1226    装箱问题
1230    寻找平面上的极大点
1231 最小新整数
1232 Crossing River
不适合初学者做的题如上 2018-5-17

1319 【例6.1】排队接水

http://blog.csdn.net/mrcrack/article/details/61625530

4.//洛谷 p1223 排队接水 
//难度:普及- 
//考点:输入,输出 ,冒泡排序,快速排序均可  
//适用:小学生 
//思考:对样例进行模拟,发现输出3 2 7 8 1 4 9 6 10 5对应1 12 33 55 56 99 99 234 812 1000马上找到该题做法。
//提交:错了测试点8,测试点10,经对比,发现,求和需采用long long sum;
//反思:快速排序是不稳定排序,为了更加准确,准备用冒泡排序重写快速排序部分。 
#include <stdio.h>
struct node{
    int t;
    int i;
}a[1000+100],a_t;
int main(){
    int n,i,j,ans;
    long long sum=0;//之前int sum=0;错了测试点8 测试点10 题目有问题,说好的10^3*10^6消失了 
    scanf(“%d”,&n);
    for(i=1;i<=n;i++){
        scanf(“%d”,&a[i].t);
        a[i].i=i;
    }
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++)
            if(a[i].t>a[j].t){
                a_t=a[i];
                a[i]=a[j];
                a[j]=a_t;
            }
    printf(“%d”,a[1].i);
    for(i=2;i<=n;i++)
        printf(” %d”,a[i].i);
    printf(“\n”);
    for(i=1;i<=n;i++)
        for(j=1;j<=i-1;j++)
            sum+=a[j].t;
    printf(“%.2f”,sum/(n*1.0));
    return 0;
}

1320 【例6.2】均分纸牌(Noip2002)

http://blog.csdn.net/mrcrack/article/details/61625530

2.洛谷 p1031 均分纸牌

NOIP2002 提高组 复赛 均分纸牌

1.看完题,第一想法就是,算出均分后的纸牌张数,与每个位置纸牌数作差,统计负的数个数,与正的数个数,取其中最大个数,即为最少移动次数,当然此种做法估计无法AC,但猜测能拿50分左右,没有其他想法的时候,就按这个办法试试吧。
2.提交,5组数据,通过2组,得分40,还算满意。

3.仔细想了想,这个思路的问题,大方向是对的,问题在于,可能负数与整数间有间隔0,那就要多移动相应0的个数,怎么考虑有0间隔呢?

4.该题还真是难在算法,感觉有小学奥数的味道。

5.还有,一个正数补负数不够,还需要其他正数帮忙,感觉此题情况比较多啊。看开一看题目定下的策略还是不错的。

6.搜索http://www.cnblogs.com/geek-007/p/5664149.html介绍得比较好,

思路:这题是一个纯模拟题,因为牌的总张数是堆的倍数,所以排好序后每队的张数就是总张数的每堆平均数(总张数÷堆数),则只需模拟一下移动的过程即可:

①从前往后扫描数组,判断距离平均数还差几张,如果小于平均数,则用后面那张补过来,如果大于平均数,则往后补

②这题可以不用排序,从前往后模拟即可,不要看题目中给的例子,那过程和我的完全不一样而且更难理解

 

7.本人用的就是上述思想,提交AC.学到一招,纯模拟

难度:想不到,很难

难度:想到,简单。

附上AC代码,编译环境Dev-C++4.9.9.2

//2002 jfzp2
#include <stdio.h>
int a[100+10];
int main(){
    int n;
    int i;
    int v;
    int zcount,fcount;
    int step;
    int t;
    scanf(“%d”,&n);
    v=0;
    for(i=1;i<=n;i++){
        scanf(“%d”,&a[i]);
        v+=a[i];
    }
    v/=n;
    step=0;
    for(i=1;i<=n-1;i++){//直接进行模拟 
        t=0;
        if(a[i]!=v){
            t=a[i]-v;
            a[i+1]+=t;
            step++;
        }
    }
    printf(“%d\n”,step);
    return 0;

 

附上40分代码,编译环境Dev-C++4.9.9.2

//2002 均分纸牌
#include <stdio.h>
int a[100+10];
int b[100+10];
int main(){
    int n;
    int i;
    int v;
    int zcount,fcount;
    int ans;
    scanf(“%d”,&n);
    v=0;
    for(i=1;i<=n;i++){
        scanf(“%d”,&a[i]);
        v+=a[i];
    }
    v/=n;
    for(i=1;i<=n;i++){
        b[i]=v-a[i];
    }
    zcount=0;
    fcount=0;
    for(i=1;i<=n;i++)
        if(b[i]>0)
            zcount++;
        else if(b[i]<0)
            fcount++;
    if(zcount>=fcount)
        ans=zcount;
    else
        ans=fcount;
    printf(“%d\n”,ans);    
    return 0;
}

//1321 【例6.3】删数问题(Noip1994)
//洛谷 P1106 删数问题
//此题与 1231 最小新整数 基本雷同
//1231 最小新整数
//感觉题目比较简单,将最大的数由大到小删除
//样例通过,但提交,未通过
//总觉得算法不对,因为删的是最大的数,但又举不出反例
//http://blog.csdn.net/c20190102/article/details/52350828此文介绍得真不赖,摘抄如下:
//此题先看看思路:
//如果是直接删掉最大的数字,很容易便可举出反例:
//1529 1
//如果直接删最大的9,结果为152,如果删掉5,结果为129,显然删掉5才是最佳答案。
//再看一组数据:
//141519 2
//如果删最大的9,5,结果为1411,如果删掉4,5,结果为1119,显然删掉4,5才是最佳答案。
//发现什么了吗?
//先看第一组:
//1  5  1  9
//小大 小 大
//留删 留 留
//第二组:
//1   4  1  5  1   9
//小 大 小 大 小 大
//留 删 留删 留 留
//删掉的是“山峰”,也就是比后一个数大的数,且越靠前“山
//峰”越早删。
//大体思路也就一句话:删除靠前的“山峰”。
//另外,有几个坑不得不提:
//1.注意删除前导0(虽然它说每个数位都不为0,但是测试数据里面好像有这样的数据)。
//2.删过一个数记得长度len–。
//3.有多组数据(其实数组可以不清零,因为有len控制查找范围)。
//4.当把数删为0(见数据4)时,要输出0。
//另外送大家几组数据(我就在此栽过跟头):
//   输入                       输出
//133420 2                   120
//1444 3                        1
//20018 2                      1
//10000 1                      0
//http://blog.csdn.net/qq_25734657/article/details/52329863代码简练,此文也写得不错,摘抄如下:
//1243865 1怎么删呢?如果你认为是删8,那就错了。如果删8,得124365,但如果删4,得123865,哪个更小呢?毫无疑问是后者吧。那如果是1244444 5呢?最后删到124就删不掉了,所以还有一个条件,如果删了一遍,删不掉,就删去最后一个。大概意思就是这样,由于这道题没有出现有0的情况,所以我在这里暂时不讨论,可以自己想想。
//代码彻底推翻重来,样例通过,提交,未通过,
//少了break,修改,提交AC 2017-11-2 22:26
//提交,测试点2,6答案错误
//提供两组测试数据
//输入:
//20018 2
//输出:
//1
//输入:
//10000 1
//输出:
//0
//针对上述两组输入输出数据进行修改,提交AC 2017-11-26 17:13
#include <stdio.h>
#include <string.h>
char c[260];
int main(){
    int len,i,j,s;
    scanf(“%s%d”,c,&s);
    len=strlen(c);
    while(s–){
        for(i=0;i<=len-2;i++)
            if(c[i]>c[i+1]){
                for(j=i;j<=len-2;j++)
                    c[j]=c[j+1];
                break;
            }
        len–;//此处位置写错,之前写在if内部
    }
    i=0;
    while(i<=len-1&&c[i]==’0′)i++;//处理前导0
    if(i==len)printf(“0”);
    else
        for(j=i;j<=len-1;j++)
            printf(“%c”,c[j]);
    return 0;
}

 

1322 【例6.4】拦截导弹问题(Noip1999)

AC代码:

#include <stdio.h>
#include <string.h>
int a[100+10];
int t[100+10];
int main(){
    int v;
    int n=0;
    int i,j;
    int maxt;
    memset(a,0,sizeof(a));
    memset(t,0,sizeof(t));
    while(scanf(“%d”,&v)==1){
        a[n]=v;
        n++;
    }
    //上升序列
    t[0]=1;
    for(i=1;i<n;i++){
        t[i]=1;
        for(j=0;j<i;j++){
            if(a[j]<a[i]&&t[j]+1>t[i])
                t[i]=t[j]+1;
            
        }
    }
    maxt=1;
    for(i=0;i<n;i++){
        if(maxt<t[i])
            maxt=t[i];
    }
    printf(“%d\n”,maxt);
    return 0;
}
 

 

线性动态规划

http://blog.csdn.net/mrcrack/article/details/61625530

1.//洛谷 p1020 导弹拦截

 

NOIP 1999 提高组 复赛  拦截导弹

1.该题一看完,马上确定是动态规划问题,对应经典模型:最大上升子序列。

2.该题是最大下降子序列。

3.最多能拦截几枚,处理好,但最少几套系统,却不清楚,几次想采用偏分,1,2,未果

4.搜索http://blog.csdn.NET/lyhvoyage/article/details/8537049介绍得不错

由于炮弹的发射高度是递减的,如果后面的导弹的高度大于前面的高度,就不能把后面的那颗导弹拦截,若想拦截,就要增加一个拦截系统。问题的实质就是求出最长的连续递增子序列的长度。

5.不过能将几枚做好,已无精力处理几套了。

6.修改程序,样例通过,提交AC。

附上AC代码,编译环境Dev-C++4.9.9.2

 

//1999 导弹拦截
#include <stdio.h>
#include <string.h>
int a[100+10];
int d[100+10];
int t[100+10];
int main(){
    int v;
    int n=0;
    int i,j;
    int maxd,maxt;
    memset(a,0,sizeof(a));
    memset(d,0,sizeof(d));
    memset(t,0,sizeof(t));
    while(scanf(“%d”,&v)==1){
        a[n]=v;
        n++;
    }
    
    //下降序列 
    d[0]=1;
    for(i=1;i<n;i++){
        d[i]=1;
        for(j=0;j<i;j++){
            if(a[j]>a[i]&&d[j]+1>d[i])
                d[i]=d[j]+1;            
        }
    }
    
    //上升序列 
    t[0]=1;
    for(i=1;i<n;i++){
        t[i]=1;
        for(j=0;j<i;j++){
            if(a[j]<a[i]&&t[j]+1>t[i])
                t[i]=t[j]+1;
            
        }
    }
    
    maxd=1;
    maxt=1;
    for(i=0;i<n;i++){
        if(maxd<d[i])
            maxd=d[i];
        if(maxt<t[i])
            maxt=t[i];
    }
    printf(“%d\n”,maxd);
    printf(“%d\n”,maxt);
    return 0;
}

 

//1323 【例6.5】活动选择
//按结束时间自小到大排序,选择合适的活动,并进行统计
//提交,未通过,测试点3,4WA。
//前一个活动的end与后一个活动的begin能否相等,题意不明,
//接下来按照相等可以的方式进行提交,AC。2017-10-29 21:05
#include <stdio.h>
struct node{
    int begin,end;
}a[1010],a_t;
int main(){
    int n,i,j,cnt=1;
    scanf(“%d”,&n);
    for(i=1;i<=n;i++)
        scanf(“%d%d”,&a[i].begin,&a[i].end);
    for(i=1;i<=n;i++)//end自小到大
        for(j=i+1;j<=n;j++)
            if(a[i].end>a[j].end)a_t=a[i],a[i]=a[j],a[j]=a_t;
    i=1,j=2;
    while(i<=n&&j<=n){//i标记当前任务,j 标记下一个任务
        if(a[i].end<=a[j].begin)i=j,j++,cnt++;//此处写成 if(a[i].end<a[j].begin)
        else j++;
    }
    printf(“%d”,cnt);
    return 0;

//1324 【例6.6】整数区间
//样例输入输出难以弄懂
//http://blog.csdn.net/yyyyyy11123/article/details/75092565?locationNum=4&fps=1此文介绍得不错,摘抄入下 
//算法分析
//     算法模型:给n个闭区间[ai,bi], 在数轴上选尽量少的点,使每个区间内至少有一个点。
//   算法:首先按b1<=b2<=…<=bn排序。每次标记当前区间的右端点x,并右移当前区间指针,直到当前区间不包含x,再重复上述操作。
//   如下图,如果选灰色点,移动到黑色点更优。
《信息学奥赛一本通(C++版) 第二部分 基础算法 第六章 贪心算法》

//通俗的想,把区间想成一块块木板,把要找的点想成钉子,如果你的钉子很靠前的话,后面的木板就可能钉不到,每次把钉子钉在你要钉的木板最后,
//这样它就能顺便钉更多木板啦
//下面代码的看点有 多关键字快排

//样例通过,提交AC 2017-11-29
#include <stdio.h>
int a[10100],b[10100];
void quicksort(int left,int right){//自小到大
    int i=left,j=right,mid=b[(left+right)/2],t;
    while(i<=j){
        while(b[i]<mid)i++;
        while(b[j]>mid)j–;
        if(i<=j)t=b[i],b[i]=b[j],b[j]=t,t=a[i],a[i]=a[j],a[j]=t,i++,j–;
    }
    if(left<j)quicksort(left,j);
    if(i<right)quicksort(i,right);
}
int main(){
    int n,i,x,sum;
    scanf(“%d”,&n);
    for(i=1;i<=n;i++)scanf(“%d%d”,&a[i],&b[i]);
    quicksort(1,n);
    x=b[1],sum=1;
    for(i=2;i<=n;i++)
        if(x<a[i])x=b[i],sum++;
    printf(“%d”,sum);
    return 0;
}

//1223 An Easy Problem
//http://blog.csdn.net/ligt0610/article/details/7262757此文思路写得不错,摘抄如下:
//一般最容易想到的方法就是先计算正整数N用二进制表示时1的个数count1,然后不停地计算N++用二进制表示时1的个数count2,直到碰到count1 == count2成立
//https://www.cnblogs.com/EdSheeran/p/7858214.html此文代码写的不错
//样例通过,提交AC 2017-11-29  
#include <stdio.h>
int bit(int a){//数二进制中1的个数
    int ans=0;
    while(a){
        if(a&1)ans++;
        a/=2;
    }
    return ans;
}
int main(){
    int b,i,n;
    while(scanf(“%d”,&b)&&b){
        n=bit(b);
        for(i=b+1;;i++)
            if(n==bit(i)){
                printf(“%d\n”,i);
                break;
            }
    }
    return 0;
}

//1224 最大子矩阵

//1282 最大子矩阵

#include <stdio.h>
#include <string.h>
int a[110][110],b[110],n;
int maxArray(int c[]){
    int d=0,i,max=-999999999;
    for(i=1;i<=n;i++){
        if(d>0)d=d+c[i];
        else d=c[i];
        if(d>max)max=d;
    }
    return max;//此处写成 return d; 出昏招,查了25分钟
}
int main(){
    int i,j,k,max=-999999999,t;
    scanf(“%d”,&n);
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            scanf(“%d”,&a[i][j]);
    for(i=1;i<=n;i++){//矩阵扫描思路,1->n,2->n,3->n,4->n,……
        memset(b,0,sizeof(b));
        for(j=i;j<=n;j++){
            for(k=1;k<=n;k++)
                b[k]+=a[j][k];
            t=maxArray(b);
            if(t>max)max=t;
        }
    }
    printf(“%d”,max);
    return 0;
}
 

//https://www.cnblogs.com/GodA/p/5237061.html此文介绍得不错,摘抄如下,略做修改
//如果有一个一维数组a[n],如何找出连续的一段,使其元素之和最大呢?
//例如有 1 2 -3 4 -2 5 -3 -1 7 4 -6 这样一个数组,那么显然 4 -2 5 -3 -1 7 4 这个子数组元素之和最大,为4+(-2)+5+(-3)+(-3)+7+4=14。为找到一维数组的最大子数组,我们可以有以下方法。

//输入数据:

11
1 2 -3 4 -2 5 -3 -1 7 4 -6
 

//输出数据:

14

//1.穷举法

#include <stdio.h>
int a[1000];
int main(){
    int n,i,j,k,sum,max=-999999999;
    scanf(“%d”,&n);
    for(i=1;i<=n;i++)scanf(“%d”,&a[i]);
    for(i=1;i<=n;i++)
        for(j=1;j<=i;j++){
            sum=0;
            for(k=j;k<=i;k++)sum+=a[k];
            if(sum>max)max=sum;
        }
    printf(“%d”,max);
    return 0;
}

//2.带记忆的递推法 ,个人感觉方法应该叫前缀和
#include <stdio.h>
int a[1000],b[1000];//b[i]前缀和,i之前元素的和,包括i元素
int main(){
    int n,i,j,k,sum,max=-999999999;
    scanf(“%d”,&n);
    b[0]=0;
    for(i=1;i<=n;i++)scanf(“%d”,&a[i]),b[i]=b[i-1]+a[i];
    for(i=1;i<=n;i++)
        for(j=0;j<i;j++){
            sum=b[i]-b[j];
            if(sum>max)max=sum;
        }
    printf(“%d”,max);
    return 0;
}

 

//3动态规划
//我们来分析一下最优子结构,若想找到n个数的最大子段和,那么要找到n-1个数的最大子段和,这就出来了。我们用b[i]来表示a[0]…a[i]的最大子段和,b[i]无非有两种情况
//:(1)最大子段一直连续到a[i]  (2)以a[i]为首的新的子段 。由此我们可以得到b[i]的状态转移方程:b[i]=max{b[i-1]+a[i],a[i]}。最终我们得到的最大子段和为max{b[i], 0<=i<n}, 算法如下:
#include <stdio.h>
int a[1000],b; //b[i]必须要选元素i,对应的最大字段和
int main(){
    int n,i,j,k,sum,max=-999999999;
    scanf(“%d”,&n);
    b=0;
    for(i=1;i<=n;i++){
        scanf(“%d”,&a[i]);
        if(b>0)b+=a[i];
        else b=a[i];//b<=0
        if(b>max)max=b;
    }
    printf(“%d”,max);
    return 0;

 

}

//1225 金银岛
//性价比最高排序,进行选取
//样例通过,提交AC 2017-11-28
#include <stdio.h>
struct node{
    int w,c;
    double g;
}q[110],q_t;
int main(){
    int t,W,m,i,j,k,w,c;
    double g,ans;
    scanf(“%d”,&t);
    while(t–){
        ans=0;
        scanf(“%d%d”,&W,&m);
        for(i=1;i<=m;i++){
            scanf(“%d%d”,&w,&c);
            g=c*1.0/w;
            q[i].w=w,q[i].c=c,q[i].g=g;
        }
        for(i=1;i<=m;i++)//冒泡排序 g由大到小
            for(j=i+1;j<=m;j++)
                if(q[i].g<q[j].g)q_t=q[i],q[i]=q[j],q[j]=q_t;
        for(i=1;i<=m;i++)
            if(W>=q[i].w)ans+=q[i].c,W-=q[i].w;
            else{
                ans+=q[i].g*W;
                break;
            }
        printf(“%.2lf\n”,ans);
    }
    return 0;
}

//1226 装箱问题
//先装大的箱子,由大到小装入空箱
//箱子空间由最小边长决定
//仔细想了想,上述思路不对
//搜索网络,发现要分类讨论,对思维层次要求比较高,类似奥数
//http://blog.csdn.net/aidway/article/details/50710070此文思路写得最棒,摘抄如下
//算法:模拟
//1.对f个6*6的产品,需要f个箱子
//2.对e个5*5的产品,需要e个箱子,另外还能放11个1*1的产品(注:每个箱子)
//3.对d个4*4的产品,需要d个箱子,另外还能放5个2*2的产品(注:每个箱子)
//4.对c个3*3的产品,需要ceil(c/3.0)个箱子,另外,
//  如果 c%4 == 0,箱子完全使用,无剩余
//  如果 c%4 == 1,箱子还能放5个2*2的产品
//  如果 c%4 == 2,箱子还能放3个2*2的产品
//  如果 c%4 == 3,箱子还能放1个2*2的产品
//5.对b个2*2的箱子,由3和4可算出能可存放的个数,如果不能完全放下,再计算还需要多少个新的箱子
//6.对a个1*1的箱子,使用面积法:总面积为36*n(n为前5步算出的箱子数),已用面积为36*f+25*e+16*d+9*c+4*b,
//  用总的面积减去已用面积,可知已用的箱子中还能存放多少个1*1的产品,如果不能完全放下,再计算还需要多少个新的箱子。  
//http://blog.csdn.net/acm_10000h/article/details/41018785此文代码写得不错
//http://blog.csdn.net/dongfengkuayue/article/details/6461374此文注释写得不错
//编码,样例通过,提交AC 2017-11-28
#include <stdio.h>
int g[]={0,5,3,1};
int main(){
    int a,b,c,d,e,f,n=0,x,y;//n总个数 x放2*2产品的个数 y剩下的面积
    while(scanf(“%d%d%d%d%d%d”,&a,&b,&c,&d,&e,&f)){
        if(a+b+c+d+e+f==0)break;
        n=f+e+d+(c+3)/4;
        x=5*d+g[c%4];
        if(b>x)n+=(b-x+8)/9;
        y=36*n-36*f-25*e-16*d-9*c-4*b;
        if(a>y)n+=(a-y+35)/36;//此处写成if(a>x)n+=(a-x+35)/36;尽出昏招
        printf(“%d\n”,n);
    }
    return 0;
}

//1227 Ride to Office
//样例的输入输出对不上,
//查了英文原题,发现,题目表述不清:
//Vi is a positive integer <= 40, indicating the speed of the i-th rider (kph, kilometers per hour). Ti is the set off time of the i-th rider, which is an integer and counted in seconds. In any case it is assured that there always exists a nonnegative Ti. 
//速度单位km/h,时间单位s 
//样例通过,提交AC 2017-11-29 
#include <stdio.h>
struct node{
    int v,s,e;//s开始时间 e结束时间 
}q[10100];
int main(){
    int n,i,v,s,e,e1,t;
    while(scanf(“%d”,&n)&&n){
        for(i=1;i<=n;i++){
            scanf(“%d%d”,&v,&s);
            t=16200/v;//4.5/v*3600==16200/v 
            if(16200.0/v>t)t+=1;
            e=s+t;
            q[i].v=v,q[i].s=s,q[i].e=e;
        }
        e1=999999999;
        for(i=1;i<=n;i++)//找到0时刻出发,对应的最短时间 
            if(q[i].s==0&&q[i].e<e1)e1=q[i].e;
        for(i=1;i<=n;i++)//找到0时刻之后出发,对应的最短时间 
            if(q[i].s>0&&q[i].e<e1)e1=q[i].e;
        printf(“%d\n”,e1);
    }
    return 0;
}

//1228 书架
//奶牛数量<=20000,故冒泡排序超时,只能快速排序
#include <stdio.h>
int a[20100];
void quicksort(int left,int right){//自大到小
    int i=left,j=right,mid=a[(left+right)/2],t;
    while(i<=j){
        while(a[i]>mid)i++;
        while(a[j]<mid)j–;
        if(i<=j)t=a[i],a[i]=a[j],a[j]=t,i++,j–;
    }
    if(left<j)quicksort(left,j);
    if(i<right)quicksort(i,right);
}
int main(){
    int n,b,i,cnt=0,sum=0;
    scanf(“%d%d”,&n,&b);
    for(i=1;i<=n;i++)scanf(“%d”,&a[i]);
    quicksort(1,n);
    for(i=1;i<=n;i++){
        sum+=a[i];
        if(sum>=b)break;
    }
    printf(“%d”,i);    
    return 0;
}

//1229 电池的寿命
//http://blog.csdn.net/king_way/article/details/20445951此文介绍得不错,摘抄如下:
//此题乍一看貌似很难,又看看了一下poj里ac和提交的比例,一开始就想偏了一直在考虑怎么用贪心算法来解题
//(当然此题就是贪心算法,不过有很简便的解答而已)。其实对于每一组数据只要判断最大的那个数是不是比其余的数的和都要大,
//如果成立的话那当然就是剩下的所有电池与最大的电池车轮战,最大为n-1个数的和,如果不成立的话那么最大就是n个数的和的一半,
//也就是说电池是一定可以全部用完的。讲一下简单的证明过程,每次先对N个数进行排序,然后最大电池每次与其余电池PK一小时,
//如此进行下去最后必然是三种情况中的一种即(2 1 1)(1 1)(1 1 1),这三种情况都是可以用完的,所以电池必定会全部用完。
//知道了这一点程序就很简单了。
//N(2≤N≤1000)用冒泡排序即可,转念一想,没必要,只要找出最大值即可
//样例通过,提交AC
#include <stdio.h>
int a[1010];
int main(){
    int n,i,j,t,sum,max;
    while(scanf(“%d”,&n)!=EOF){
        sum=0,max=-1;
        for(i=1;i<=n;i++){
            scanf(“%d”,&a[i]);
            if(a[i]>max)max=a[i];
            sum+=a[i];
        }
        if(max>=sum-max)printf(“%.1lf\n”,(sum-max)*1.0);
        else printf(“%.1lf\n”,sum*1.0/2);
    }
    return 0;
}

//1230 寻找平面上的极大点
//样例中(1,4) (3,1) 没问题,(2,3)怎么找出,心存疑虑
//感觉思维像奥数
//http://blog.csdn.net/cqyz_holiday/article/details/52495567此文介绍得不错,摘抄入下:
//最简单的思路就是直接暴力枚举每个点,判断它的右上方是否有其它点,如果没有则该点即为极大点,这种算法的时间复杂度为O(n^2),对于n=100000的数据显然会超时。
//因n<=100,准备采用枚举的方式
//真是被这道题吓到了,没想到实践起来确实如此的简单。样例通过,提交AC 2017-11-28 18:46
#include <stdio.h>
#include <string.h>
int x[110],y[110],vis[110];
int main(){
    int n,i,j,t,cnt=0;
    memset(vis,0,sizeof(vis));
    scanf(“%d”,&n);
    for(i=1;i<=n;i++)scanf(“%d%d”,&x[i],&y[i]);
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++)
            if(x[i]>x[j])t=x[i],x[i]=x[j],x[j]=t,t=y[i],y[i]=y[j],y[j]=t;
            else if(x[i]==x[j]&&y[i]>y[j])t=x[i],x[i]=x[j],x[j]=t,t=y[i],y[i]=y[j],y[j]=t;
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            if(i!=j)
                if(x[j]>=x[i]&&y[j]>=y[i]){
                    vis[i]=1;
                    break;
                }
    for(i=1;i<=n;i++)
        if(vis[i]==0){
            cnt++;
            if(cnt==1)printf(“(%d,%d)”,x[i],y[i]);
            else printf(“,(%d,%d)”,x[i],y[i]);
        }
    return 0;
}

 

//1231 最小新整数
//感觉题目比较简单,将最大的数由大到小删除
//样例通过,但提交,未通过
//总觉得算法不对,因为删的是最大的数,但又举不出反例
//http://blog.csdn.net/c20190102/article/details/52350828此文介绍得真不赖,摘抄如下:
//此题先看看思路:
//如果是直接删掉最大的数字,很容易便可举出反例:
//1529 1
//如果直接删最大的9,结果为152,如果删掉5,结果为129,显然删掉5才是最佳答案。
//再看一组数据:
//141519 2
//如果删最大的9,5,结果为1411,如果删掉4,5,结果为1119,显然删掉4,5才是最佳答案。
//发现什么了吗?
//先看第一组:
//1  5  1  9
//小大 小 大
//留删 留 留
//第二组:
//1   4  1  5  1   9
//小 大 小 大 小 大
//留 删 留删 留 留
//删掉的是“山峰”,也就是比后一个数大的数,且越靠前“山
//峰”越早删。
//大体思路也就一句话:删除靠前的“山峰”。
//另外,有几个坑不得不提:
//1.注意删除前导0(虽然它说每个数位都不为0,但是测试数据里面好像有这样的数据)。
//2.删过一个数记得长度len–。
//3.有多组数据(其实数组可以不清零,因为有len控制查找范围)。
//4.当把数删为0(见数据4)时,要输出0。
//另外送大家几组数据(我就在此栽过跟头):
//   输入                       输出
//133420 2                   120
//1444 3                        1
//20018 2                      1
//10000 1                      0
//http://blog.csdn.net/qq_25734657/article/details/52329863代码简练,此文也写得不错,摘抄如下:
//1243865 1怎么删呢?如果你认为是删8,那就错了。如果删8,得124365,但如果删4,得123865,哪个更小呢?毫无疑问是后者吧。那如果是1244444 5呢?最后删到124就删不掉了,所以还有一个条件,如果删了一遍,删不掉,就删去最后一个。大概意思就是这样,由于这道题没有出现有0的情况,所以我在这里暂时不讨论,可以自己想想。
//代码彻底推翻重来,样例通过,提交,未通过,
//少了break,修改,提交AC 2017-11-2 22:26
#include <stdio.h>
#include <string.h>
char s[20];
int main(){
    int t,n,k,i,j,len;
    scanf(“%d”,&t);
    while(t–){//不用for循环的目的是省下变量i
        scanf(“%s%d”,s,&k);
        len=strlen(s);
        while(k–){//不用for循环的目的是省下变量i
            for(i=0;i<len-1;i++)
                if(s[i]>s[i+1]){
                    for(j=i;j<len-1;j++)
                        s[j]=s[j+1];
                    break;//少了此句 提交 未通过
                }
            len–;
        }
        s[len]=’\0′;
        printf(“%s\n”,s);
    }
    return 0;
}

//1232 Crossing River
//POJ1700 Crossing River
//题目描述不敢恭维,英文题目如下https://vjudge.net/problem/POJ-1700
//弄明白样例是关键:
//此文,样例解释得比较清晰http://blog.csdn.net/c20190101zjx/article/details/52318603摘抄如下:
//题目大意是:现在有一群人,到了一条河旁边,想要过河,但船只有一条,一次最多能载两个人,开到了对面还需要一个人负责把船开回来,而且若多人坐船,速度还是由慢的一个决定,现在求如何分配坐船,使总时间最短。刚刚看到这道题时,我还有一点惊讶,这么水的一道题,居然只有70多个人过了,只需要找到最快的一个,然后让他送每一个人到对岸就可以了,数据还不大。但不太放心的我还是模拟了一遍样例,然后吓死宝宝了,按我的思路,最短也要19秒(请无视单位),但是样例却是惊人的17秒,于是我叫来了同学们与老师一起讨论,然后得出了以下几种可能:1.回来不需要时间–怎么可能~2.我们找的不是最少的时间——瞎说,好吧还真不是瞎说,其实可以这样搭配:1和2开船过去,1回来,总用时+3,5和10开船过去,2回来,总用时+12,1和2开船过去,总用时+2,共17秒。这么说来,当人数为1,用时很明显是一个人过河的时间,人数为2,是两人中较慢的,3个人,则最快的来回接另外两个人,4个,则是最快的来回接人或者以刚才所说的方法,先送最慢的过去,看哪种更快。什么?你问我4种以上?递归取两种方案的最大值就可以了~以下是我第一次的代码:
//该题的关键,回来的人也可以是对岸的人,而不一定要是船上的人
//此文思路写得不错https://www.cnblogs.com/hankers/archive/2012/01/20/2328313.html摘抄如下:
//此题讲的是N个人过河,每个人都有自己的过河时间,一条船只能承受2个人,所用时间为其中过河时间最多的,所以呢,想到有两种情况,第一种:过河时间最少的人来回接送其他人,第二种:过河时间最少和次少的人来回接送其他人,刚开始就觉得第一种时间必然是最少的,但是仔细想想,不然。因为第一种情况虽然单次过河时间少,但送人的次数要多,如第1个人(过河时间最少)接送第i人 dp[i]=dp[i-1]+time[0]+time[i]; 而第二种情况呢,虽然来回次数多,但是接送的人要多,如第1个人接第i-1个和第i个人,然后让第i-1个和第i个人一块过河,第2个人再接送第1个人。   dp[i]=dp[i-2]+time[0]+time[i]+time[1]*2。所以每次计算出这两个值都应该进行比较,从而AC!
//提交,格式错误,重读题目,修改,提交AC 2017-11-15 17:57
#include <stdio.h>
int a[1100],f[1100];//f[i] 共i号人在对岸所花最短时间
int min(int a,int b){
    return a<b?a:b;
}
int main(){
    int t,n,i,j,b;
    scanf(“%d”,&t);
    while(t–){
        scanf(“%d”,&n);
        for(i=0;i<n;i++)scanf(“%d”,&a[i]);
        for(i=0;i<n;i++)//自小到大排序
            for(j=i+1;j<n;j++)
                if(a[i]>a[j])
                    b=a[i],a[i]=a[j],a[j]=b;
        f[0]=a[0],f[1]=a[1];
        for(i=2;i<n;i++)
            f[i]=min(f[i-1]+a[0]+a[i],f[i-2]+a[0]+a[i]+a[1]*2);
        printf(“%d\n”,f[n-1]);//此处写成 printf(“%d”,f[n-1]); 格式错误
    }
    return 0;
}

 

1233 接水问题

http://blog.csdn.net/mrcrack/article/details/60868057

 

NOIP 2010 普及组 复赛 water 接水问题

1.一开始想的是队列,但觉得比较复杂。

2.稍微想了想,接完水,马上加上下一个接水的,也就是在最少耗时的水龙头上加上下一位。找最小值

3.最终找到最长时间的水龙头,即耗时。找最大值

 

//洛谷 P1190 接水问题 
//难度:入门难度
//考点:输入,输出 ,找最大值,找最小值,函数的写法  
//适用:小学生 

附上AC代码,编译环境Dev-C++4.9.9.2

#include <stdio.h>
int a[100+10];//水龙头数 
int b[10000+10];//人数 
int findmax(int *a,int b,int e){
    int i;
    int max=0;
    int k;
    for(i=b;i<=e;i++)
        if(max<a[i]){
            max=a[i];
            k=i;
        }
    return k;
}
int findmin(int *a,int b,int e){
    int i;
    int min=999999;
    int k;
    for(i=b;i<=e;i++)
        if(min>a[i]){
            min=a[i];
            k=i;
        }
    return k;
}
int main(){
    int i,j;
    int n,m;
    scanf(“%d%d”,&n,&m);
    for(i=1;i<=n;i++)
        scanf(“%d”,&b[i]);
    if(n<=m){//水龙头数多 
        j=findmax(b,1,n);
        printf(“%d\n”,b[j]);
    }else{//人数多 
        for(i=1;i<=m;i++)
            a[i]=b[i];
        for(i=m+1;i<=n;i++){//加入后续打水学生 
            j=findmin(a,1,m);
            a[j]+=b[i];
        }
        j=findmax(a,1,m);
        printf(“%d\n”,a[j]);
    }
    return 0;
}

 

2017-11-29 AC 该章节内容

    原文作者:贪心算法
    原文地址: https://blog.csdn.net/mrcrack/article/details/78376152
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞