表示塔萌大学生智商好高,居然能搞出这么耗脑筋的题(详见C题)……先膜再发题解……
A 发工资咯:)
有面值为壹佰元、伍拾元、拾元、伍元、贰元、壹元的人民币,问付给他人
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
种物品,其中有第
思想:贪心
做法:从价值最高的物品开始,能取完则取完,直至包容积耗尽。(证明详见紫书)
注意:单价!单价!单价!
#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 个人在一条河的一岸,想过河到达对岸,河边只有一条船,船每次只能载两人,每个人过河所需时间不同,两人一起过河所耗时间为过河较慢的人所需花费的时间,问使所有人过河的最短时间。
思想:贪心
思路:
当
由于最快的人需要多次陪别人过河,并且把船划回来,所以最快的人耗费的时间总和难以直接计算,所以我们应当从最慢的人开始考虑。
显而易见,要让最慢和次慢的人都过河有两种安排方式:
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
条线段,第
思路:贪心
做法:先将所有线段排序,先按右端点升序排列,再按左端点降序排列,然后从左到右依次判断线段是否与已有线段覆盖,若不覆盖则选择此线段。
#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
满足
思路一:前缀和+二分查找(这两天学的全用上……),时间复杂度 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;
}
尾声
看清题!看清题!看清题!
充智商!充智商!充智商!