CCNU ACM 2016夏季集训·day3比赛
2016-07-13 20:25
435 查看
表示塔萌大学生智商好高,居然能搞出这么耗脑筋的题(详见C题)……先膜再发题解……
思想:贪心
知识:生活常识
思想:贪心
做法:从价值最高的物品开始,能取完则取完,直至包容积耗尽。(证明详见紫书)
注意:单价!单价!单价!
思想:贪心
思路:
当n≤3时问题很简单,但当n≥4时问题变得就复杂起来……
由于最快的人需要多次陪别人过河,并且把船划回来,所以最快的人耗费的时间总和难以直接计算,所以我们应当从最慢的人开始考虑。
显而易见,要让最慢和次慢的人都过河有两种安排方式:
1. 分别让最快的人陪两人过河,并且每次都由最快的人把船划回来
2. (这就是我一直没想到的关键)让最快与次快的人过河,次快的人把船划回来(此时最快的人留在对岸),最慢和次慢的人把船划到对岸,此时由最快的人把船划回。最终效果是节省了一次最快划船的时间加上次慢划船的时间,减去两次次快划船的时间
做法:每次选择两种方式中节省时间的一种,最快和次快因为把船划回来所以仍停留在岸边,而最慢和次慢以最优方案渡河,问题规模减小2,重复上述过程直至问题规模降至3以内。
感想:简直神思路(贵poj,真!就不得了……),容我再想想如何证明……
思路:贪心
做法:先将所有线段排序,先按右端点升序排列,再按左端点降序排列,然后从左到右依次判断线段是否与已有线段覆盖,若不覆盖则选择此线段。
思想:贪心
做法:类似背包问题贪心算法。
思路一:前缀和+二分查找(这两天学的全用上……),时间复杂度O(nlogn)
思路二:(膜拜神犇魏子卿)前缀和,利用答案单调性,时间复杂度O(n)
充智商!充智商!充智商!
A 发工资咯:)
有面值为壹佰元、伍拾元、拾元、伍元、贰元、壹元的人民币,问付给他人x元(x为正整数)最少需多少纸币。思想:贪心
知识:生活常识
#include <cstdio> #include <algorithm> #include <vector> #define NMAX 100 #define M 6 int 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 100 struct 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个人在一条河的一岸,想过河到达对岸,河边只有一条船,船每次只能载两人,每个人过河所需时间不同,两人一起过河所耗时间为过河较慢的人所需花费的时间,问使所有人过河的最短时间。思想:贪心
思路:
当n≤3时问题很简单,但当n≥4时问题变得就复杂起来……
由于最快的人需要多次陪别人过河,并且把船划回来,所以最快的人耗费的时间总和难以直接计算,所以我们应当从最慢的人开始考虑。
显而易见,要让最慢和次慢的人都过河有两种安排方式:
1. 分别让最快的人陪两人过河,并且每次都由最快的人把船划回来
2. (这就是我一直没想到的关键)让最快与次快的人过河,次快的人把船划回来(此时最快的人留在对岸),最慢和次慢的人把船划到对岸,此时由最快的人把船划回。最终效果是节省了一次最快划船的时间加上次慢划船的时间,减去两次次快划船的时间
做法:每次选择两种方式中节省时间的一种,最快和次快因为把船划回来所以仍停留在岸边,而最慢和次慢以最优方案渡河,问题规模减小2,重复上述过程直至问题规模降至3以内。
感想:简直神思路(贵poj,真!就不得了……),容我再想想如何证明……
#include <cstdio> #include <algorithm> using namespace std; #define NMAX 1000 int 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 100 struct 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 20 struct 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 100000 int 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 100000 int 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; }
尾声
看清题!看清题!看清题!充智商!充智商!充智商!
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++高级程序员成长之路
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C++的template模板中class与typename关键字的区别分析
- C与C++之间相互调用实例方法讲解