您的位置:首页 > 编程语言 > C语言/C++

CCNU ACM 2016夏季集训·day3比赛

2016-07-13 20:25 435 查看
表示塔萌大学生智商好高,居然能搞出这么耗脑筋的题(详见C题)……先膜再发题解……

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;
}


尾声

看清题!看清题!看清题!

充智商!充智商!充智商!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息