CCNU ACM 2016夏季集训·day3比赛

表示塔萌大学生智商好高,居然能搞出这么耗脑筋的题(详见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 个人在一条河的一岸,想过河到达对岸,河边只有一条船,船每次只能载两人,每个人过河所需时间不同,两人一起过河所耗时间为过河较慢的人所需花费的时间,问使所有人过河的最短时间。

思想:贪心
思路:
n3时问题很简单,但当 n4 时问题变得就复杂起来……
由于最快的人需要多次陪别人过河,并且把船划回来,所以最快的人耗费的时间总和难以直接计算,所以我们应当从最慢的人开始考虑。
显而易见,要让最慢和次慢的人都过河有两种安排方式:
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;
}

尾声

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值