CCNU ACM 2016夏季集训·day3比赛

来源:互联网 发布:苏州天魂网络面试题 编辑:程序博客网 时间:2024/05/14 09:32
表示塔萌大学生智商好高,居然能搞出这么耗脑筋的题(详见C题)……先膜再发题解……

A 发工资咯:)

有面值为壹佰元、伍拾元、拾元、伍元、贰元、壹元的人民币,问付给他人x元(x为正整数)最少需多少纸币。

思想:贪心
知识:生活常识

#include <cstdio>#include <algorithm>#include <vector>#define NMAX 100#define M 6int v[M]={100,50,10,5,2,1};using namespace std;int main(){    int n;    int tmp,num;    int i,j;    while(true){        scanf("%d",&n);        if(!n)return 0;        num=0;        for(i=0;i<n;i++){            scanf("%d",&tmp);            for(j=0;j<M;j++){                num+=tmp/v[j];                tmp%=v[j];            }        }        printf("%d\n",num);    }    return 0;}

B Saving HDU

现有n种物品,其中有第i种物品mi个单位体积,该种物品单价(单位体积价值)为pi,问一个容积为vi的包最多能装多大价值的物品。(一种物品可以只取一部分)

思想:贪心
做法:从价值最高的物品开始,能取完则取完,直至包容积耗尽。(证明详见紫书)
注意:单价!单价!单价!

#include <cstdio>#include <algorithm>#include <vector>#include <cmath>using namespace std;#define NMAX 100struct th{    int p,m;    bool operator < (const th b)const{return p>b.p;}};th ths[NMAX];int main(){    int v,n,nv;    int ans;    int i;    while(true){        scanf("%d",&v);        if(!v)return 0;        scanf("%d",&n);        for(i=0;i<n;i++){            scanf("%d%d",&ths[i].p,&ths[i].m);            //ths[i].v=ths[i].p/(tmp=ths[i].m);        }        sort(ths,ths+n);        ans=0;        for(i=0;i<n;i++){            nv=min(ths[i].m,v);            ans+=nv*ths[i].p;            v-=nv;        }        printf("%d\n",ans);    }    return 0;}

C Crossing River

现有n个人在一条河的一岸,想过河到达对岸,河边只有一条船,船每次只能载两人,每个人过河所需时间不同,两人一起过河所耗时间为过河较慢的人所需花费的时间,问使所有人过河的最短时间。

思想:贪心
思路:
n3时问题很简单,但当n4时问题变得就复杂起来……
由于最快的人需要多次陪别人过河,并且把船划回来,所以最快的人耗费的时间总和难以直接计算,所以我们应当从最慢的人开始考虑。
显而易见,要让最慢和次慢的人都过河有两种安排方式:
1. 分别让最快的人陪两人过河,并且每次都由最快的人把船划回来
2. (这就是我一直没想到的关键)让最快与次快的人过河,次快的人把船划回来(此时最快的人留在对岸),最慢和次慢的人把船划到对岸,此时由最快的人把船划回。最终效果是节省了一次最快划船的时间加上次慢划船的时间,减去两次次快划船的时间
做法:每次选择两种方式中节省时间的一种,最快和次快因为把船划回来所以仍停留在岸边,而最慢和次慢以最优方案渡河,问题规模减小2,重复上述过程直至问题规模降至3以内。
感想:简直神思路(贵poj,真!就不得了……),容我再想想如何证明……

#include <cstdio>#include <algorithm>using namespace std;#define NMAX 1000int a[NMAX];int main(){    int t,n;    int ans;    int ybz;    int i;    scanf("%d",&t);    for(ybz=0;ybz<t;ybz++){        scanf("%d",&n);        for(i=0;i<n;i++)            scanf("%d",a+i);        sort(a,a+n);        for(ans=0;n>3;n-=2)            ans+=min(a[n-1]+a[n-2]+a[0]*2,a[n-1]+a[0]+a[1]*2);        if(n==1)ans+=a[0];        else if(n==2)ans+=a[1];        else ans+=a[0]+a[1]+a[2];        printf("%d\n",ans);    }    return 0;}

D 今年暑假不AC

数轴上有n条线段,第i条线段左右端点分别为ai,bi,现要从中选出若干条线段,使他们相互不覆盖,问最多能选出多少线段。

思路:贪心
做法:先将所有线段排序,先按右端点升序排列,再按左端点降序排列,然后从左到右依次判断线段是否与已有线段覆盖,若不覆盖则选择此线段。

#include <cstdio>#include <algorithm>#include <vector>using namespace std;#define NMAX 100struct seg{    int s,t;    bool operator < (const seg b)const{return (t==b.t)?(s>b.s):(t<b.t);}};seg ss[NMAX];int main(){    int n;    int last,num;    int i;    while(true){        scanf("%d",&n);        if(!n)return 0;        for(i=0;i<n;i++)            scanf("%d%d",&ss[i].s,&ss[i].t);        sort(ss,ss+n);        last=num=0;        for(i=0;i<n;i++)            if(last<=ss[i].s){                num++;                last=ss[i].t;            }        printf("%d\n",num);    }    return 0;}

E Hero

在一场dota游戏中,你坚定不移地想装个b单挑对面所有英雄,每个英雄有自己的dps和hp,你的英雄dps只有1,而hp无限。你每次可以挑战一个英雄,使他的hp降低1(若hp降低为零,则英雄死亡),而同时你要承受所有未死亡英雄的攻击,即hp要降低对方所有未死亡英雄dps之和,问要杀死对方所有英雄,hp最少降低多少。

思想:贪心
做法:类似背包问题贪心算法。

 #include <cstdio>#include <algorithm>#include <vector>#include <cmath>using namespace std;#define NMAX 20struct hero{    int dps,hp;    double v;    bool operator < (const hero b)const{return v>b.v;}};hero ls[NMAX];int main(){    int n;    int ans,t;    int i;    while(scanf("%d",&n)!=EOF){        for(i=0;i<n;i++){            scanf("%d%d",&ls[i].dps,&ls[i].hp);            ls[i].v=ls[i].dps/(double)ls[i].hp;        }        sort(ls,ls+n);        ans=t=0;        for(i=0;i<n;i++){            ans+=(ls[i].hp+t)*ls[i].dps;            t+=ls[i].hp;        }        printf("%d\n",ans);    }    return 0;}

F 子序列

输入一段序列和整数S0,求原序列的一段最短的连续子序列,使子序列所有项的和S满足S>S0

思路一:前缀和+二分查找(这两天学的全用上……),时间复杂度O(nlogn)

#include <cstdio>#include <algorithm>#include <vector>using namespace std;#define NMAX 100000int a[NMAX+1],sum[NMAX+1];int main(){    int t,n,s;    int l,r,mid;    int mi;    int ybz;    int i;    scanf("%d",&t);    for(ybz=0;ybz<t;ybz++){        scanf("%d%d",&n,&s);        for(i=1;i<=n;i++){            scanf("%d",a+i);            sum[i]=sum[i-1]+a[i];        }        mi=n+1;        for(i=1;i<=n;i++){            l=0;            r=i-1;            while(l!=r){                mid=(l+r)/2;                if(sum[mid]+s>sum[i])r=mid;                else l=mid+1;            }            if((l+1<i)&&(sum[l+1]+s<=sum[i])){if(i-l-1<mi)mi=i-l-1;}            if(sum[l]+s<=sum[i]){if(i-l<mi)mi=i-l;}            else{if((l-1>=0)&&(i-l+1<mi))mi=i-l+1;}        }        printf("%d\n",(mi==n+1)?0:mi);    }    return 0;}

思路二:(膜拜神犇魏子卿)前缀和,利用答案单调性,时间复杂度O(n)

#include <cstdio>#define NMAX 100000int a[NMAX+1],sum[NMAX+1];int main(){    int t,n,s;    int mi;    int i,j;    for(scanf("%d",&t);t;t--){        scanf("%d%d",&n,&s);        for(i=1;i<=n;i++){            scanf("%d",a+i);            sum[i]=sum[i-1]+a[i];        }        mi=n+1;        j=0;        for(i=1;i<=n;i++){            for(;sum[i]-sum[j+1]>=s;j++);            //printf("%d %d\n",i,j);            if((sum[i]-sum[j]>=s)&&(i-j<mi))mi=i-j;        }        printf("%d\n",(mi==n+1)?0:mi);    }    return 0;}

尾声

看清题!看清题!看清题!
充智商!充智商!充智商!

0 0